mirror of
https://github.com/django/django.git
synced 2025-01-18 22:33:44 +00:00
Fixed #20910 -- Added a "snippet" sphinx directive to allow prefixing a filename.
Thanks Marc Tamlyn for the suggestion.
This commit is contained in:
parent
e077224f4a
commit
d07d6ae116
1
AUTHORS
1
AUTHORS
@ -282,6 +282,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Scot Hacker <shacker@birdhouse.org>
|
Scot Hacker <shacker@birdhouse.org>
|
||||||
dAniel hAhler
|
dAniel hAhler
|
||||||
hambaloney
|
hambaloney
|
||||||
|
Nasimul Haque <nasim.haque@gmail.com>
|
||||||
Will Hardy <django@willhardy.com.au>
|
Will Hardy <django@willhardy.com.au>
|
||||||
Brian Harring <ferringb@gmail.com>
|
Brian Harring <ferringb@gmail.com>
|
||||||
Brant Harris
|
Brant Harris
|
||||||
|
@ -5,11 +5,15 @@ import json
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
from docutils.parsers.rst import directives
|
||||||
|
|
||||||
from sphinx import addnodes, __version__ as sphinx_ver
|
from sphinx import addnodes, __version__ as sphinx_ver
|
||||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||||
from sphinx.writers.html import SmartyPantsHTMLTranslator
|
from sphinx.writers.html import SmartyPantsHTMLTranslator
|
||||||
from sphinx.util.console import bold
|
from sphinx.util.console import bold
|
||||||
from sphinx.util.compat import Directive
|
from sphinx.util.compat import Directive
|
||||||
|
from sphinx.util.nodes import set_source_info
|
||||||
|
|
||||||
# RE for option descriptions without a '--' prefix
|
# RE for option descriptions without a '--' prefix
|
||||||
simple_option_desc_re = re.compile(
|
simple_option_desc_re = re.compile(
|
||||||
@ -53,6 +57,136 @@ def setup(app):
|
|||||||
app.add_directive('versionchanged', VersionDirective)
|
app.add_directive('versionchanged', VersionDirective)
|
||||||
app.add_builder(DjangoStandaloneHTMLBuilder)
|
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('<div class="snippet-filename">%s</div>\n''' % (fname,))
|
||||||
|
self.body.append(highlighted)
|
||||||
|
self.body.append('</div>\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):
|
class VersionDirective(Directive):
|
||||||
has_content = True
|
has_content = True
|
||||||
|
3
docs/_theme/djangodocs/static/djangodocs.css
vendored
3
docs/_theme/djangodocs/static/djangodocs.css
vendored
@ -100,6 +100,9 @@ pre { font-size:small; background:#E0FFB8; border:1px solid #94da3a; border-widt
|
|||||||
dt .literal, table .literal { background:none; }
|
dt .literal, table .literal { background:none; }
|
||||||
#bd a.reference { text-decoration: none; }
|
#bd a.reference { text-decoration: none; }
|
||||||
#bd a.reference tt.literal { border-bottom: 1px #234f32 dotted; }
|
#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 */
|
/* 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; }
|
#bd .highlight .k a:link, #bd .highlight .k a:visited { color: #000000; text-decoration: none; border-bottom: 1px dotted #000000; }
|
||||||
|
@ -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
|
1. First, create a parent directory for ``polls``, outside of your Django
|
||||||
project. Call this directory ``django-polls``.
|
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
|
When choosing a name for your package, check resources like PyPI to avoid
|
||||||
naming conflicts with existing packages. It's often useful to prepend
|
naming conflicts with existing packages. It's often useful to prepend
|
||||||
``django-`` to your module name when creating a package to distribute.
|
``django-`` to your module name when creating a package to distribute.
|
||||||
This helps others looking for Django apps identify your app as Django
|
This helps others looking for Django apps identify your app as Django
|
||||||
specific.
|
specific.
|
||||||
|
|
||||||
2. Move the ``polls`` directory into the ``django-polls`` directory.
|
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:
|
||||||
|
|
||||||
=====
|
.. snippet::
|
||||||
Polls
|
: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 = (
|
1. Add "polls" to your INSTALLED_APPS setting like this::
|
||||||
...
|
|
||||||
'polls',
|
|
||||||
)
|
|
||||||
|
|
||||||
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/
|
3. Run `python manage.py migrate` to create the polls models.
|
||||||
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. 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
|
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
|
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
|
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
|
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
|
license. Just be aware that your licensing choice will affect who is able
|
||||||
to use your code.
|
to use your code.
|
||||||
|
|
||||||
5. Next we'll create a ``setup.py`` file which provides details about how to
|
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
|
build and install the app. A full explanation of this file is beyond the
|
||||||
scope of this tutorial, but the `distribute docs
|
scope of this tutorial, but the `distribute docs
|
||||||
<http://packages.python.org/distribute/setuptools.html>`_ have a good explanation.
|
<http://packages.python.org/distribute/setuptools.html>`_ have a good
|
||||||
Create a file ``django-polls/setup.py`` with the following contents::
|
explanation. Create a file ``django-polls/setup.py`` with the following
|
||||||
|
contents:
|
||||||
|
|
||||||
import os
|
.. snippet::
|
||||||
from setuptools import setup
|
: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
|
README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read()
|
||||||
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
|
|
||||||
|
|
||||||
setup(
|
# allow setup.py to be run from any path
|
||||||
name='django-polls',
|
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
|
||||||
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',
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
.. 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
|
.. admonition:: I thought you said we were going to use ``distribute``?
|
||||||
appear to import from ``setuptools``, since we have ``distribute``
|
|
||||||
installed, it will override the import.
|
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
|
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
|
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
|
distribute docs referred to in the previous step discuss this file in more
|
||||||
details. To include the templates, the ``README.rst`` and our ``LICENSE``
|
details. To include the templates, the ``README.rst`` and our ``LICENSE``
|
||||||
file, create a file ``django-polls/MANIFEST.in`` with the following
|
file, create a file ``django-polls/MANIFEST.in`` with the following
|
||||||
contents::
|
contents:
|
||||||
|
|
||||||
include LICENSE
|
.. snippet::
|
||||||
include README.rst
|
:filename: django-polls/MANIFEST.in
|
||||||
recursive-include polls/static *
|
|
||||||
recursive-include polls/templates *
|
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
|
7. It's optional, but recommended, to include detailed documentation with your
|
||||||
app. Create an empty directory ``django-polls/docs`` for future
|
app. Create an empty directory ``django-polls/docs`` for future
|
||||||
|
@ -344,7 +344,10 @@ the text of the choice and a vote tally. Each ``Choice`` is associated with a
|
|||||||
``Question``.
|
``Question``.
|
||||||
|
|
||||||
These concepts are represented by simple Python classes. Edit the
|
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
|
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
|
Edit the :file:`mysite/settings.py` file again, and change the
|
||||||
:setting:`INSTALLED_APPS` setting to include the string ``'polls'``. So it'll
|
: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 = (
|
INSTALLED_APPS = (
|
||||||
'django.contrib.admin',
|
'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
|
``polls/models.py`` file) and adding a
|
||||||
:meth:`~django.db.models.Model.__unicode__` method to both ``Question`` and
|
:meth:`~django.db.models.Model.__unicode__` method to both ``Question`` and
|
||||||
``Choice``. On Python 3, simply replace ``__unicode__`` by ``__str__`` in the
|
``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
|
from django.db import models
|
||||||
|
|
||||||
@ -633,7 +642,10 @@ admin.
|
|||||||
luck, things should Just Work for you.
|
luck, things should Just Work for you.
|
||||||
|
|
||||||
Note these are normal Python methods. Let's add a custom method, just for
|
Note these are normal Python methods. Let's add a custom method, just for
|
||||||
demonstration::
|
demonstration:
|
||||||
|
|
||||||
|
.. snippet::
|
||||||
|
:filename: polls/models.py
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -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``
|
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`
|
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 django.contrib import admin
|
||||||
from polls.models import Question
|
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.
|
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
|
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 django.contrib import admin
|
||||||
from polls.models import Question
|
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.
|
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
|
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 django.contrib import admin
|
||||||
from polls.models import Question
|
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
|
You can assign arbitrary HTML classes to each fieldset. Django provides a
|
||||||
``"collapse"`` class that displays a particular fieldset initially collapsed.
|
``"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
|
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 django.contrib import admin
|
||||||
from polls.models import Question
|
from polls.models import Question
|
||||||
@ -228,7 +240,10 @@ the admin page doesn't display choices.
|
|||||||
Yet.
|
Yet.
|
||||||
|
|
||||||
There are two ways to solve this problem. The first is to register ``Choice``
|
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 django.contrib import admin
|
||||||
from polls.models import Choice
|
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.
|
``Question`` object. Let's make that happen.
|
||||||
|
|
||||||
Remove the ``register()`` call for the ``Choice`` model. Then, edit the ``Question``
|
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 django.contrib import admin
|
||||||
from polls.models import Choice, Question
|
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
|
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
|
fields for entering related ``Choice`` objects. For that reason, Django offers a
|
||||||
tabular way of displaying inline related objects; you just need to change
|
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):
|
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
|
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
|
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
|
``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):
|
class QuestionAdmin(admin.ModelAdmin):
|
||||||
# ...
|
# ...
|
||||||
list_display = ('question_text', 'pub_date')
|
list_display = ('question_text', 'pub_date')
|
||||||
|
|
||||||
Just for good measure, let's also include the ``was_published_recently`` custom
|
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):
|
class QuestionAdmin(admin.ModelAdmin):
|
||||||
# ...
|
# ...
|
||||||
@ -356,7 +383,10 @@ underscores replaced with spaces), and that each line contains the string
|
|||||||
representation of the output.
|
representation of the output.
|
||||||
|
|
||||||
You can improve that by giving that method (in :file:`polls/models.py`) a few
|
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):
|
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.
|
project is a good convention to follow.
|
||||||
|
|
||||||
Open your settings file (:file:`mysite/settings.py`, remember) and add a
|
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')]
|
TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')]
|
||||||
|
|
||||||
|
@ -84,7 +84,10 @@ Your app directory should now look like::
|
|||||||
urls.py
|
urls.py
|
||||||
views.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
|
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
|
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
|
``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
|
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
|
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):
|
def detail(request, question_id):
|
||||||
return HttpResponse("You're looking at question %s." % 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)
|
return HttpResponse("You're voting on question %s." % question_id)
|
||||||
|
|
||||||
Wire these new views into the ``polls.urls`` module by adding the following
|
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
|
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
|
Because it's convenient, let's use Django's own database API, which we covered
|
||||||
in :doc:`Tutorial 1 </intro/tutorial01>`. Here's one stab at the ``index()``
|
in :doc:`Tutorial 1 </intro/tutorial01>`. Here's one stab at the ``index()``
|
||||||
view, which displays the latest 5 poll questions in the system, separated by
|
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
|
from django.http import HttpResponse
|
||||||
|
|
||||||
@ -327,7 +342,8 @@ Django simply as ``polls/index.html``.
|
|||||||
|
|
||||||
Put the following code in that template:
|
Put the following code in that template:
|
||||||
|
|
||||||
.. code-block:: html+django
|
.. snippet:: html+django
|
||||||
|
:filename: polls/templates/polls/index.html
|
||||||
|
|
||||||
{% if latest_question_list %}
|
{% if latest_question_list %}
|
||||||
<ul>
|
<ul>
|
||||||
@ -339,7 +355,10 @@ Put the following code in that template:
|
|||||||
<p>No polls are available.</p>
|
<p>No polls are available.</p>
|
||||||
{% endif %}
|
{% 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.http import HttpResponse
|
||||||
from django.template import RequestContext, loader
|
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
|
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
|
:class:`~django.http.HttpResponse` object with the result of the rendered
|
||||||
template. Django provides a shortcut. Here's the full ``index()`` view,
|
template. Django provides a shortcut. Here's the full ``index()`` view,
|
||||||
rewritten::
|
rewritten:
|
||||||
|
|
||||||
|
.. snippet::
|
||||||
|
:filename: polls/views.py
|
||||||
|
|
||||||
from django.shortcuts import render
|
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
|
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.http import Http404
|
||||||
from django.shortcuts import render
|
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
|
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
|
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 }}
|
{{ 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`
|
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
|
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
|
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
|
variable ``question``, here's what the ``polls/detail.html`` template might look
|
||||||
like:
|
like:
|
||||||
|
|
||||||
.. code-block:: html+django
|
.. snippet:: html+django
|
||||||
|
:filename: polls/templates/polls/detail.html
|
||||||
|
|
||||||
<h1>{{ question.question_text }}</h1>
|
<h1>{{ question.question_text }}</h1>
|
||||||
<ul>
|
<ul>
|
||||||
@ -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``
|
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
|
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
|
from django.conf.urls import patterns, include, url
|
||||||
|
|
||||||
@ -563,13 +598,15 @@ it to include namespacing::
|
|||||||
|
|
||||||
Now change your ``polls/index.html`` template from:
|
Now change your ``polls/index.html`` template from:
|
||||||
|
|
||||||
.. code-block:: html+django
|
.. snippet:: html+django
|
||||||
|
:filename: polls/templates/polls/index.html
|
||||||
|
|
||||||
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
|
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
|
||||||
|
|
||||||
to point at the namespaced detail view:
|
to point at the namespaced detail view:
|
||||||
|
|
||||||
.. code-block:: html+django
|
.. snippet:: html+django
|
||||||
|
:filename: polls/templates/polls/index.html
|
||||||
|
|
||||||
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
|
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@ Write a simple form
|
|||||||
Let's update our poll detail template ("polls/detail.html") from the last
|
Let's update our poll detail template ("polls/detail.html") from the last
|
||||||
tutorial, so that the template contains an HTML ``<form>`` element:
|
tutorial, so that the template contains an HTML ``<form>`` element:
|
||||||
|
|
||||||
.. code-block:: html+django
|
.. snippet:: html+django
|
||||||
|
:filename: polls/templates/polls/detail.html
|
||||||
|
|
||||||
<h1>{{ question.question_text }}</h1>
|
<h1>{{ question.question_text }}</h1>
|
||||||
|
|
||||||
@ -54,12 +55,18 @@ A quick rundown:
|
|||||||
|
|
||||||
Now, let's create a Django view that handles the submitted data and does
|
Now, let's create a Django view that handles the submitted data and does
|
||||||
something with it. Remember, in :doc:`Tutorial 3 </intro/tutorial03>`, we
|
something with it. Remember, in :doc:`Tutorial 3 </intro/tutorial03>`, 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<question_id>\d+)/vote/$', views.vote, name='vote'),
|
url(r'^(?P<question_id>\d+)/vote/$', views.vote, name='vote'),
|
||||||
|
|
||||||
We also created a dummy implementation of the ``vote()`` function. Let's
|
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.shortcuts import get_object_or_404, render
|
||||||
from django.http import HttpResponseRedirect, HttpResponse
|
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 </ref/request-response>`.
|
:doc:`request and response documentation </ref/request-response>`.
|
||||||
|
|
||||||
After somebody votes in a question, the ``vote()`` view redirects to the results
|
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
|
from django.shortcuts import get_object_or_404, render
|
||||||
|
|
||||||
@ -149,7 +159,8 @@ redundancy later.
|
|||||||
|
|
||||||
Now, create a ``polls/results.html`` template:
|
Now, create a ``polls/results.html`` template:
|
||||||
|
|
||||||
.. code-block:: html+django
|
.. snippet:: html+django
|
||||||
|
:filename: polls/templates/polls/results.html
|
||||||
|
|
||||||
<h1>{{ question.question_text }}</h1>
|
<h1>{{ question.question_text }}</h1>
|
||||||
|
|
||||||
@ -205,7 +216,10 @@ Read on for details.
|
|||||||
Amend URLconf
|
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
|
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``
|
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
|
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.shortcuts import get_object_or_404, render
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
|
@ -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
|
``tests.py`` file; the testing system will automatically find tests in any file
|
||||||
whose name begins with ``test``.
|
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
|
import datetime
|
||||||
|
|
||||||
@ -236,7 +239,10 @@ Fixing the bug
|
|||||||
We already know what the problem is: ``Question.was_published_recently()`` should
|
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
|
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
|
``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):
|
def was_published_recently(self):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
@ -268,7 +274,10 @@ method; in fact, it would be positively embarrassing if in fixing one bug we had
|
|||||||
introduced another.
|
introduced another.
|
||||||
|
|
||||||
Add two more test methods to the same class, to test the behavior of the method
|
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):
|
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.
|
``pub_date`` in the future). Let's fix that.
|
||||||
|
|
||||||
In :doc:`Tutorial 4 </intro/tutorial04>` we introduced a class-based view,
|
In :doc:`Tutorial 4 </intro/tutorial04>` 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):
|
class IndexView(generic.ListView):
|
||||||
template_name = 'polls/index.html'
|
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
|
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
|
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
|
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):
|
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
|
change that might affect this* - so let's also create a test, based on our
|
||||||
:djadmin:`shell` session above.
|
: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
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
and we'll create a factory method to create questions as well as a new test
|
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):
|
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
|
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
|
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):
|
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
|
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``
|
``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):
|
class QuestionIndexDetailTests(TestCase):
|
||||||
def test_detail_view_with_a_future_question(self):
|
def test_detail_view_with_a_future_question(self):
|
||||||
|
@ -56,7 +56,8 @@ reference the path for templates.
|
|||||||
|
|
||||||
Put the following code in that stylesheet (``polls/static/polls/style.css``):
|
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 {
|
li a {
|
||||||
color: green;
|
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``:
|
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 %}
|
{% 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``):
|
Then, add to your stylesheet (``polls/static/polls/style.css``):
|
||||||
|
|
||||||
.. code-block:: css
|
.. snippet:: css
|
||||||
|
:filename: polls/static/polls/style.css
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: white url("images/background.gif") no-repeat right bottom;
|
background: white url("images/background.gif") no-repeat right bottom;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user