From d07d6ae1167f93f2e88b3743c070003a66a31b35 Mon Sep 17 00:00:00 2001 From: M Nasimul Haque Date: Mon, 23 Sep 2013 23:23:47 +0100 Subject: [PATCH] Fixed #20910 -- Added a "snippet" sphinx directive to allow prefixing a filename. Thanks Marc Tamlyn for the suggestion. --- AUTHORS | 1 + docs/_ext/djangodocs.py | 134 ++++++++++++++++ docs/_theme/djangodocs/static/djangodocs.css | 3 + docs/intro/reusable-apps.txt | 158 ++++++++++--------- docs/intro/tutorial01.txt | 20 ++- docs/intro/tutorial02.txt | 55 +++++-- docs/intro/tutorial03.txt | 67 ++++++-- docs/intro/tutorial04.txt | 31 +++- docs/intro/tutorial05.txt | 49 ++++-- docs/intro/tutorial06.txt | 9 +- 10 files changed, 403 insertions(+), 124 deletions(-) diff --git a/AUTHORS b/AUTHORS index 83d3e4fbc1..c7490182bf 100644 --- a/AUTHORS +++ b/AUTHORS @@ -282,6 +282,7 @@ answer newbie questions, and generally made Django that much better: Scot Hacker dAniel hAhler hambaloney + Nasimul Haque Will Hardy Brian Harring Brant Harris diff --git a/docs/_ext/djangodocs.py b/docs/_ext/djangodocs.py index e7e316f58b..1c1ed53672 100644 --- a/docs/_ext/djangodocs.py +++ b/docs/_ext/djangodocs.py @@ -5,11 +5,15 @@ import json import os import re +from docutils import nodes +from docutils.parsers.rst import directives + from sphinx import addnodes, __version__ as sphinx_ver from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.writers.html import SmartyPantsHTMLTranslator from sphinx.util.console import bold from sphinx.util.compat import Directive +from sphinx.util.nodes import set_source_info # RE for option descriptions without a '--' prefix simple_option_desc_re = re.compile( @@ -53,6 +57,136 @@ def setup(app): app.add_directive('versionchanged', VersionDirective) app.add_builder(DjangoStandaloneHTMLBuilder) + # register the snippet directive + app.add_directive('snippet', SnippetWithFilename) + # register a node for snippet directive so that the xml parser + # knows how to handle the enter/exit parsing event + app.add_node(snippet_with_filename, + html=(visit_snippet, depart_snippet_literal), + latex=(visit_snippet_latex, depart_snippet_latex), + man=(visit_snippet_literal, depart_snippet_literal), + text=(visit_snippet_literal, depart_snippet_literal), + texinfo=(visit_snippet_literal, depart_snippet_literal)) + + +class snippet_with_filename(nodes.literal_block): + """ + Subclass the literal_block to override the visit/depart event handlers + """ + pass + + +def visit_snippet_literal(self, node): + """ + default literal block handler + """ + self.visit_literal_block(node) + + +def depart_snippet_literal(self, node): + """ + default literal block handler + """ + self.depart_literal_block(node) + + +def visit_snippet(self, node): + """ + HTML document generator visit handler + """ + lang = self.highlightlang + linenos = node.rawsource.count('\n') >= self.highlightlinenothreshold - 1 + fname = node['filename'] + highlight_args = node.get('highlight_args', {}) + if node.has_key('language'): + # code-block directives + lang = node['language'] + highlight_args['force'] = True + if node.has_key('linenos'): + linenos = node['linenos'] + + def warner(msg): + self.builder.warn(msg, (self.builder.current_docname, node.line)) + + highlighted = self.highlighter.highlight_block(node.rawsource, lang, + warn=warner, + linenos=linenos, + **highlight_args) + starttag = self.starttag(node, 'div', suffix='', + CLASS='highlight-%s' % lang) + self.body.append(starttag) + self.body.append('
%s
\n''' % (fname,)) + self.body.append(highlighted) + self.body.append('\n') + raise nodes.SkipNode + + +def visit_snippet_latex(self, node): + """ + Latex document generator visit handler + """ + self.verbatim = '' + + +def depart_snippet_latex(self, node): + """ + Latex document generator depart handler. + """ + code = self.verbatim.rstrip('\n') + lang = self.hlsettingstack[-1][0] + linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1 + fname = node['filename'] + highlight_args = node.get('highlight_args', {}) + if 'language' in node: + # code-block directives + lang = node['language'] + highlight_args['force'] = True + if 'linenos' in node: + linenos = node['linenos'] + + def warner(msg): + self.builder.warn(msg, (self.curfilestack[-1], node.line)) + + hlcode = self.highlighter.highlight_block(code, lang, warn=warner, + linenos=linenos, + **highlight_args) + + self.body.append('\n{\\colorbox[rgb]{0.9,0.9,0.9}' + '{\\makebox[\\textwidth][l]' + '{\\small\\texttt{%s}}}}\n' % (fname,)) + + if self.table: + hlcode = hlcode.replace('\\begin{Verbatim}', + '\\begin{OriginalVerbatim}') + self.table.has_problematic = True + self.table.has_verbatim = True + + hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim} + hlcode = hlcode.rstrip() + '\n' + self.body.append('\n' + hlcode + '\\end{%sVerbatim}\n' % + (self.table and 'Original' or '')) + self.verbatim = None + + +class SnippetWithFilename(Directive): + """ + The 'snippet' directive that allows to add the filename (optional) + of a code snippet in the document. This is modeled after CodeBlock. + """ + has_content = True + optional_arguments = 1 + option_spec = {'filename': directives.unchanged_required} + + def run(self): + code = u'\n'.join(self.content) + + literal = snippet_with_filename(code, code) + if self.arguments: + literal['language'] = self.arguments[0] + literal['filename'] = self.options['filename'] + set_source_info(self, literal) + return [literal] + class VersionDirective(Directive): has_content = True diff --git a/docs/_theme/djangodocs/static/djangodocs.css b/docs/_theme/djangodocs/static/djangodocs.css index 86ac07adb4..2b5288625b 100644 --- a/docs/_theme/djangodocs/static/djangodocs.css +++ b/docs/_theme/djangodocs/static/djangodocs.css @@ -100,6 +100,9 @@ pre { font-size:small; background:#E0FFB8; border:1px solid #94da3a; border-widt dt .literal, table .literal { background:none; } #bd a.reference { text-decoration: none; } #bd a.reference tt.literal { border-bottom: 1px #234f32 dotted; } +div.snippet-filename { color: white; background-color: #234F32; margin: 0; padding: 2px 5px; width: 100%; font-family: monospace; font-size: small; line-height: 1.3em; } +div.snippet-filename + div.highlight > pre { margin-top: 0; } +div.snippet-filename + pre { margin-top: 0; } /* Restore colors of pygments hyperlinked code */ #bd .highlight .k a:link, #bd .highlight .k a:visited { color: #000000; text-decoration: none; border-bottom: 1px dotted #000000; } diff --git a/docs/intro/reusable-apps.txt b/docs/intro/reusable-apps.txt index e42d8bcb3e..589d3fa0ee 100644 --- a/docs/intro/reusable-apps.txt +++ b/docs/intro/reusable-apps.txt @@ -120,112 +120,122 @@ this. For a small app like polls, this process isn't too difficult. 1. First, create a parent directory for ``polls``, outside of your Django project. Call this directory ``django-polls``. -.. admonition:: Choosing a name for your app + .. admonition:: Choosing a name for your app - When choosing a name for your package, check resources like PyPI to avoid - naming conflicts with existing packages. It's often useful to prepend - ``django-`` to your module name when creating a package to distribute. - This helps others looking for Django apps identify your app as Django - specific. + When choosing a name for your package, check resources like PyPI to avoid + naming conflicts with existing packages. It's often useful to prepend + ``django-`` to your module name when creating a package to distribute. + This helps others looking for Django apps identify your app as Django + specific. 2. Move the ``polls`` directory into the ``django-polls`` directory. -3. Create a file ``django-polls/README.rst`` with the following contents:: +3. Create a file ``django-polls/README.rst`` with the following contents: - ===== - Polls - ===== + .. snippet:: + :filename: django-polls/README.rst - Polls is a simple Django app to conduct Web-based polls. For each - question, visitors can choose between a fixed number of answers. + ===== + Polls + ===== - Detailed documentation is in the "docs" directory. + Polls is a simple Django app to conduct Web-based polls. For each + question, visitors can choose between a fixed number of answers. - Quick start - ----------- + Detailed documentation is in the "docs" directory. - 1. Add "polls" to your INSTALLED_APPS setting like this:: + Quick start + ----------- - INSTALLED_APPS = ( - ... - 'polls', - ) + 1. Add "polls" to your INSTALLED_APPS setting like this:: - 2. Include the polls URLconf in your project urls.py like this:: + INSTALLED_APPS = ( + ... + 'polls', + ) - url(r'^polls/', include('polls.urls')), + 2. Include the polls URLconf in your project urls.py like this:: - 3. Run `python manage.py migrate` to create the polls models. + url(r'^polls/', include('polls.urls')), - 4. Start the development server and visit http://127.0.0.1:8000/admin/ - to create a poll (you'll need the Admin app enabled). + 3. Run `python manage.py migrate` to create the polls models. - 5. Visit http://127.0.0.1:8000/polls/ to participate in the poll. + 4. Start the development server and visit http://127.0.0.1:8000/admin/ + to create a poll (you'll need the Admin app enabled). + + 5. Visit http://127.0.0.1:8000/polls/ to participate in the poll. 4. Create a ``django-polls/LICENSE`` file. Choosing a license is beyond the -scope of this tutorial, but suffice it to say that code released publicly -without a license is *useless*. Django and many Django-compatible apps are -distributed under the BSD license; however, you're free to pick your own -license. Just be aware that your licensing choice will affect who is able -to use your code. + scope of this tutorial, but suffice it to say that code released publicly + without a license is *useless*. Django and many Django-compatible apps are + distributed under the BSD license; however, you're free to pick your own + license. Just be aware that your licensing choice will affect who is able + to use your code. 5. Next we'll create a ``setup.py`` file which provides details about how to -build and install the app. A full explanation of this file is beyond the -scope of this tutorial, but the `distribute docs -`_ have a good explanation. -Create a file ``django-polls/setup.py`` with the following contents:: + build and install the app. A full explanation of this file is beyond the + scope of this tutorial, but the `distribute docs + `_ have a good + explanation. Create a file ``django-polls/setup.py`` with the following + contents: - import os - from setuptools import setup + .. snippet:: + :filename: django-polls/setup.py - README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read() + import os + from setuptools import setup - # allow setup.py to be run from any path - os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) + README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read() - setup( - name='django-polls', - version='0.1', - packages=['polls'], - include_package_data=True, - license='BSD License', # example license - description='A simple Django app to conduct Web-based polls.', - long_description=README, - url='http://www.example.com/', - author='Your Name', - author_email='yourname@example.com', - classifiers=[ - 'Environment :: Web Environment', - 'Framework :: Django', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', # example license - 'Operating System :: OS Independent', - 'Programming Language :: Python', - # replace these appropriately if you are using Python 3 - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Topic :: Internet :: WWW/HTTP', - 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', - ], - ) + # allow setup.py to be run from any path + os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) -.. admonition:: I thought you said we were going to use ``distribute``? + setup( + name='django-polls', + version='0.1', + packages=['polls'], + include_package_data=True, + license='BSD License', # example license + description='A simple Django app to conduct Web-based polls.', + long_description=README, + url='http://www.example.com/', + author='Your Name', + author_email='yourname@example.com', + classifiers=[ + 'Environment :: Web Environment', + 'Framework :: Django', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', # example license + 'Operating System :: OS Independent', + 'Programming Language :: Python', + # replace these appropriately if you are using Python 3 + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', + ], + ) - Distribute is a drop-in replacement for ``setuptools``. Even though we - appear to import from ``setuptools``, since we have ``distribute`` - installed, it will override the import. + .. admonition:: I thought you said we were going to use ``distribute``? + + Distribute is a drop-in replacement for ``setuptools``. Even though we + appear to import from ``setuptools``, since we have ``distribute`` + installed, it will override the import. 6. Only Python modules and packages are included in the package by default. To include additional files, we'll need to create a ``MANIFEST.in`` file. The distribute docs referred to in the previous step discuss this file in more details. To include the templates, the ``README.rst`` and our ``LICENSE`` file, create a file ``django-polls/MANIFEST.in`` with the following - contents:: + contents: - include LICENSE - include README.rst - recursive-include polls/static * - recursive-include polls/templates * + .. snippet:: + :filename: django-polls/MANIFEST.in + + include LICENSE + include README.rst + recursive-include polls/static * + recursive-include polls/templates * 7. It's optional, but recommended, to include detailed documentation with your app. Create an empty directory ``django-polls/docs`` for future diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt index dd40a47548..8ddfc96edc 100644 --- a/docs/intro/tutorial01.txt +++ b/docs/intro/tutorial01.txt @@ -344,7 +344,10 @@ the text of the choice and a vote tally. Each ``Choice`` is associated with a ``Question``. These concepts are represented by simple Python classes. Edit the -:file:`polls/models.py` file so it looks like this:: +:file:`polls/models.py` file so it looks like this: + +.. snippet:: + :filename: polls/models.py from django.db import models @@ -415,7 +418,10 @@ But first we need to tell our project that the ``polls`` app is installed. Edit the :file:`mysite/settings.py` file again, and change the :setting:`INSTALLED_APPS` setting to include the string ``'polls'``. So it'll -look like this:: +look like this: + +.. snippet:: + :filename: mysite/settings.py INSTALLED_APPS = ( 'django.contrib.admin', @@ -589,7 +595,10 @@ of this object. Let's fix that by editing the ``Question`` model (in the ``polls/models.py`` file) and adding a :meth:`~django.db.models.Model.__unicode__` method to both ``Question`` and ``Choice``. On Python 3, simply replace ``__unicode__`` by ``__str__`` in the -following example:: +following example: + +.. snippet:: + :filename: polls/models.py from django.db import models @@ -633,7 +642,10 @@ admin. luck, things should Just Work for you. Note these are normal Python methods. Let's add a custom method, just for -demonstration:: +demonstration: + +.. snippet:: + :filename: polls/models.py import datetime from django.utils import timezone diff --git a/docs/intro/tutorial02.txt b/docs/intro/tutorial02.txt index 152d637054..710ae57737 100644 --- a/docs/intro/tutorial02.txt +++ b/docs/intro/tutorial02.txt @@ -79,7 +79,10 @@ But where's our poll app? It's not displayed on the admin index page. Just one thing to do: we need to tell the admin that ``Question`` objects have an admin interface. To do this, open the :file:`polls/admin.py` -file, and edit it to look like this:: +file, and edit it to look like this: + +.. snippet:: + :filename: polls/admin.py from django.contrib import admin from polls.models import Question @@ -156,7 +159,10 @@ to customize how the admin form looks and works. You'll do this by telling Django the options you want when you register the object. Let's see how this works by re-ordering the fields on the edit form. Replace -the ``admin.site.register(Question)`` line with:: +the ``admin.site.register(Question)`` line with: + +.. snippet:: + :filename: polls/admin.py from django.contrib import admin from polls.models import Question @@ -181,7 +187,10 @@ This isn't impressive with only two fields, but for admin forms with dozens of fields, choosing an intuitive order is an important usability detail. And speaking of forms with dozens of fields, you might want to split the form -up into fieldsets:: +up into fieldsets: + +.. snippet:: + :filename: polls/admin.py from django.contrib import admin from polls.models import Question @@ -204,7 +213,10 @@ Here's what our form looks like now: You can assign arbitrary HTML classes to each fieldset. Django provides a ``"collapse"`` class that displays a particular fieldset initially collapsed. This is useful when you have a long form that contains a number of fields that -aren't commonly used:: +aren't commonly used: + +.. snippet:: + :filename: polls/admin.py from django.contrib import admin from polls.models import Question @@ -228,7 +240,10 @@ the admin page doesn't display choices. Yet. There are two ways to solve this problem. The first is to register ``Choice`` -with the admin just as we did with ``Question``. That's easy:: +with the admin just as we did with ``Question``. That's easy: + +.. snippet:: + :filename: polls/admin.py from django.contrib import admin from polls.models import Choice @@ -258,7 +273,10 @@ It'd be better if you could add a bunch of Choices directly when you create the ``Question`` object. Let's make that happen. Remove the ``register()`` call for the ``Choice`` model. Then, edit the ``Question`` -registration code to read:: +registration code to read: + +.. snippet:: + :filename: polls/admin.py from django.contrib import admin from polls.models import Choice, Question @@ -302,7 +320,10 @@ that you can't remove the original three slots. This image shows an added slot: One small problem, though. It takes a lot of screen space to display all the fields for entering related ``Choice`` objects. For that reason, Django offers a tabular way of displaying inline related objects; you just need to change -the ``ChoiceInline`` declaration to read:: +the ``ChoiceInline`` declaration to read: + +.. snippet:: + :filename: polls/admin.py class ChoiceInline(admin.TabularInline): #... @@ -330,14 +351,20 @@ Here's what it looks like at this point: By default, Django displays the ``str()`` of each object. But sometimes it'd be more helpful if we could display individual fields. To do that, use the ``list_display`` admin option, which is a tuple of field names to display, as -columns, on the change list page for the object:: +columns, on the change list page for the object: + +.. snippet:: + :filename: polls/admin.py class QuestionAdmin(admin.ModelAdmin): # ... list_display = ('question_text', 'pub_date') Just for good measure, let's also include the ``was_published_recently`` custom -method from Tutorial 1:: +method from Tutorial 1: + +.. snippet:: + :filename: polls/admin.py class QuestionAdmin(admin.ModelAdmin): # ... @@ -356,7 +383,10 @@ underscores replaced with spaces), and that each line contains the string representation of the output. You can improve that by giving that method (in :file:`polls/models.py`) a few -attributes, as follows:: +attributes, as follows: + +.. snippet:: + :filename: polls/admin.py class Question(models.Model): # ... @@ -417,7 +447,10 @@ whatever user your server runs.) However, keeping your templates within the project is a good convention to follow. Open your settings file (:file:`mysite/settings.py`, remember) and add a -:setting:`TEMPLATE_DIRS` setting:: +:setting:`TEMPLATE_DIRS` setting: + +.. snippet:: + :filename: mysite/settings.py TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')] diff --git a/docs/intro/tutorial03.txt b/docs/intro/tutorial03.txt index 06707f2da8..2e0159f1db 100644 --- a/docs/intro/tutorial03.txt +++ b/docs/intro/tutorial03.txt @@ -84,7 +84,10 @@ Your app directory should now look like:: urls.py views.py -In the ``polls/urls.py`` file include the following code:: +In the ``polls/urls.py`` file include the following code: + +.. snippet:: + :filename: polls/urls.py from django.conf.urls import patterns, url @@ -96,7 +99,10 @@ In the ``polls/urls.py`` file include the following code:: The next step is to point the root URLconf at the ``polls.urls`` module. In ``mysite/urls.py`` insert an :func:`~django.conf.urls.include`, leaving you -with:: +with: + +.. snippet:: + :filename: mysite/urls.py from django.conf.urls import patterns, include, url @@ -172,7 +178,10 @@ Writing more views ================== Now let's add a few more views to ``polls/views.py``. These views are -slightly different, because they take an argument:: +slightly different, because they take an argument: + +.. snippet:: + :filename: polls/views.py def detail(request, question_id): return HttpResponse("You're looking at question %s." % question_id) @@ -185,7 +194,10 @@ slightly different, because they take an argument:: return HttpResponse("You're voting on question %s." % question_id) Wire these new views into the ``polls.urls`` module by adding the following -:func:`~django.conf.urls.url` calls:: +:func:`~django.conf.urls.url` calls: + +.. snippet:: + :filename: polls/urls.py from django.conf.urls import patterns, url @@ -269,7 +281,10 @@ All Django wants is that :class:`~django.http.HttpResponse`. Or an exception. Because it's convenient, let's use Django's own database API, which we covered in :doc:`Tutorial 1 `. Here's one stab at the ``index()`` view, which displays the latest 5 poll questions in the system, separated by -commas, according to publication date:: +commas, according to publication date: + +.. snippet:: + :filename: polls/views.py from django.http import HttpResponse @@ -327,7 +342,8 @@ Django simply as ``polls/index.html``. Put the following code in that template: -.. code-block:: html+django +.. snippet:: html+django + :filename: polls/templates/polls/index.html {% if latest_question_list %}
    @@ -339,7 +355,10 @@ Put the following code in that template:

    No polls are available.

    {% endif %} -Now let's update our ``index`` view in ``polls/views.py`` to use the template:: +Now let's update our ``index`` view in ``polls/views.py`` to use the template: + +.. snippet:: + :filename: polls/views.py from django.http import HttpResponse from django.template import RequestContext, loader @@ -369,7 +388,10 @@ A shortcut: :func:`~django.shortcuts.render` It's a very common idiom to load a template, fill a context and return an :class:`~django.http.HttpResponse` object with the result of the rendered template. Django provides a shortcut. Here's the full ``index()`` view, -rewritten:: +rewritten: + +.. snippet:: + :filename: polls/views.py from django.shortcuts import render @@ -395,7 +417,10 @@ Raising a 404 error =================== Now, let's tackle the question detail view -- the page that displays the question text -for a given poll. Here's the view:: +for a given poll. Here's the view: + +.. snippet:: + :filename: polls/views.py from django.http import Http404 from django.shortcuts import render @@ -414,7 +439,10 @@ if a question with the requested ID doesn't exist. We'll discuss what you could put in that ``polls/detail.html`` template a bit later, but if you'd like to quickly get the above example working, a file -containing just:: +containing just: + +.. snippet:: html+django + :filename: polls/templates/polls/detail.html {{ question }} @@ -425,7 +453,10 @@ A shortcut: :func:`~django.shortcuts.get_object_or_404` It's a very common idiom to use :meth:`~django.db.models.query.QuerySet.get` and raise :exc:`~django.http.Http404` if the object doesn't exist. Django -provides a shortcut. Here's the ``detail()`` view, rewritten:: +provides a shortcut. Here's the ``detail()`` view, rewritten: + +.. snippet:: + :filename: polls/views.py from django.shortcuts import render, get_object_or_404 @@ -466,7 +497,8 @@ Back to the ``detail()`` view for our poll application. Given the context variable ``question``, here's what the ``polls/detail.html`` template might look like: -.. code-block:: html+django +.. snippet:: html+django + :filename: polls/templates/polls/detail.html

    {{ question.question_text }}

      @@ -549,7 +581,10 @@ make it so that Django knows which app view to create for a url when using the The answer is to add namespaces to your root URLconf. In the ``mysite/urls.py`` file (the project's ``urls.py``, not the application's), go ahead and change -it to include namespacing:: +it to include namespacing: + +.. snippet:: + :filename: mysite/urls.py from django.conf.urls import patterns, include, url @@ -563,13 +598,15 @@ it to include namespacing:: Now change your ``polls/index.html`` template from: -.. code-block:: html+django +.. snippet:: html+django + :filename: polls/templates/polls/index.html
    • {{ question.question_text }}
    • to point at the namespaced detail view: -.. code-block:: html+django +.. snippet:: html+django + :filename: polls/templates/polls/index.html
    • {{ question.question_text }}
    • diff --git a/docs/intro/tutorial04.txt b/docs/intro/tutorial04.txt index 8abf2d395a..f7aa79e6f7 100644 --- a/docs/intro/tutorial04.txt +++ b/docs/intro/tutorial04.txt @@ -12,7 +12,8 @@ Write a simple form Let's update our poll detail template ("polls/detail.html") from the last tutorial, so that the template contains an HTML ``
      `` element: -.. code-block:: html+django +.. snippet:: html+django + :filename: polls/templates/polls/detail.html

      {{ question.question_text }}

      @@ -54,12 +55,18 @@ A quick rundown: Now, let's create a Django view that handles the submitted data and does something with it. Remember, in :doc:`Tutorial 3 `, we -created a URLconf for the polls application that includes this line:: +created a URLconf for the polls application that includes this line: + +.. snippet:: + :filename: polls/urls.py url(r'^(?P\d+)/vote/$', views.vote, name='vote'), We also created a dummy implementation of the ``vote()`` function. Let's -create a real version. Add the following to ``polls/views.py``:: +create a real version. Add the following to ``polls/views.py``: + +.. snippet:: + :filename: polls/views.py from django.shortcuts import get_object_or_404, render from django.http import HttpResponseRedirect, HttpResponse @@ -134,7 +141,10 @@ object. For more on :class:`~django.http.HttpRequest` objects, see the :doc:`request and response documentation `. After somebody votes in a question, the ``vote()`` view redirects to the results -page for the question. Let's write that view:: +page for the question. Let's write that view: + +.. snippet:: + :filename: polls/views.py from django.shortcuts import get_object_or_404, render @@ -149,7 +159,8 @@ redundancy later. Now, create a ``polls/results.html`` template: -.. code-block:: html+django +.. snippet:: html+django + :filename: polls/templates/polls/results.html

      {{ question.question_text }}

      @@ -205,7 +216,10 @@ Read on for details. Amend URLconf ------------- -First, open the ``polls/urls.py`` URLconf and change it like so:: +First, open the ``polls/urls.py`` URLconf and change it like so: + +.. snippet:: + :filename: polls/urls.py from django.conf.urls import patterns, url @@ -223,7 +237,10 @@ Amend views Next, we're going to remove our old ``index``, ``detail``, and ``results`` views and use Django's generic views instead. To do so, open the -``polls/views.py`` file and change it like so:: +``polls/views.py`` file and change it like so: + +.. snippet:: + :filename: polls/views.py from django.shortcuts import get_object_or_404, render from django.http import HttpResponseRedirect diff --git a/docs/intro/tutorial05.txt b/docs/intro/tutorial05.txt index 123ce5c280..90d3894738 100644 --- a/docs/intro/tutorial05.txt +++ b/docs/intro/tutorial05.txt @@ -160,7 +160,10 @@ A conventional place for an application's tests is in the application's ``tests.py`` file; the testing system will automatically find tests in any file whose name begins with ``test``. -Put the following in the ``tests.py`` file in the ``polls`` application:: +Put the following in the ``tests.py`` file in the ``polls`` application: + +.. snippet:: + :filename: polls/tests.py import datetime @@ -236,7 +239,10 @@ Fixing the bug We already know what the problem is: ``Question.was_published_recently()`` should return ``False`` if its ``pub_date`` is in the future. Amend the method in ``models.py``, so that it will only return ``True`` if the date is also in the -past:: +past: + +.. snippet:: + :filename: polls/models.py def was_published_recently(self): now = timezone.now() @@ -268,7 +274,10 @@ method; in fact, it would be positively embarrassing if in fixing one bug we had introduced another. Add two more test methods to the same class, to test the behavior of the method -more comprehensively:: +more comprehensively: + +.. snippet:: + :filename: polls/tests.py def test_was_published_recently_with_old_question(self): """ @@ -382,7 +391,10 @@ The list of polls shows polls that aren't published yet (i.e. those that have a ``pub_date`` in the future). Let's fix that. In :doc:`Tutorial 4 ` we introduced a class-based view, -based on :class:`~django.views.generic.list.ListView`:: +based on :class:`~django.views.generic.list.ListView`: + +.. snippet:: + :filename: polls/views.py class IndexView(generic.ListView): template_name = 'polls/index.html' @@ -397,11 +409,17 @@ places into the context. We need to amend the ``get_queryset`` method and change it so that it also checks the date by comparing it with ``timezone.now()``. First we need to add -an import:: +an import: + +.. snippet:: + :filename: polls/views.py from django.utils import timezone -and then we must amend the ``get_queryset`` method like so:: +and then we must amend the ``get_queryset`` method like so: + +.. snippet:: + :filename: polls/views.py def get_queryset(self): """ @@ -426,12 +444,18 @@ are listed. You don't want to have to do that *every single time you make any change that might affect this* - so let's also create a test, based on our :djadmin:`shell` session above. -Add the following to ``polls/tests.py``:: +Add the following to ``polls/tests.py``: + +.. snippet:: + :filename: polls/tests.py from django.core.urlresolvers import reverse and we'll create a factory method to create questions as well as a new test -class:: +class: + +.. snippet:: + :filename: polls/tests.py def create_question(question_text, days): """ @@ -532,8 +556,10 @@ Testing the ``DetailView`` What we have works well; however, even though future questions don't appear in the *index*, users can still reach them if they know or guess the right URL. So -we need to add a similar constraint to ``DetailView``:: +we need to add a similar constraint to ``DetailView``: +.. snippet:: + :filename: polls/views.py class DetailView(generic.DetailView): ... @@ -545,7 +571,10 @@ we need to add a similar constraint to ``DetailView``:: And of course, we will add some tests, to check that a ``Question`` whose ``pub_date`` is in the past can be displayed, and that one with a ``pub_date`` -in the future is not:: +in the future is not: + +.. snippet:: + :filename: polls/tests.py class QuestionIndexDetailTests(TestCase): def test_detail_view_with_a_future_question(self): diff --git a/docs/intro/tutorial06.txt b/docs/intro/tutorial06.txt index 2a1567915d..a88c33026a 100644 --- a/docs/intro/tutorial06.txt +++ b/docs/intro/tutorial06.txt @@ -56,7 +56,8 @@ reference the path for templates. Put the following code in that stylesheet (``polls/static/polls/style.css``): -.. code-block:: css +.. snippet:: css + :filename: polls/static/polls/style.css li a { color: green; @@ -64,7 +65,8 @@ Put the following code in that stylesheet (``polls/static/polls/style.css``): Next, add the following at the top of ``polls/templates/polls/index.html``: -.. code-block:: html+django +.. snippet:: html+django + :filename: polls/templates/polls/index.html {% load staticfiles %} @@ -88,7 +90,8 @@ called ``background.gif``. In other words, put your image in Then, add to your stylesheet (``polls/static/polls/style.css``): -.. code-block:: css +.. snippet:: css + :filename: polls/static/polls/style.css body { background: white url("images/background.gif") no-repeat right bottom;