Refs #20910 -- Replaced snippet directive with code-block.

This commit is contained in:
Curtis Maloney 2018-09-11 03:00:34 +10:00 committed by Tim Graham
parent f8ff529ee3
commit c49ea6f591
32 changed files with 234 additions and 375 deletions

View File

@ -6,14 +6,13 @@ import os
import re
from docutils import nodes
from docutils.parsers.rst import Directive, directives
from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
from sphinx import addnodes
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.directives import CodeBlock
from sphinx.domains.std import Cmdoption
from sphinx.util.console import bold
from sphinx.util.nodes import set_source_info
from sphinx.writers.html import HTMLTranslator
# RE for option descriptions without a '--' prefix
@ -53,17 +52,6 @@ def setup(app):
app.add_directive('versionadded', VersionDirective)
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))
app.set_translator('djangohtml', DjangoHTMLTranslator)
app.set_translator('json', DjangoHTMLTranslator)
app.add_node(
@ -79,133 +67,6 @@ def setup(app):
return {'parallel_read_safe': True}
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 '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.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 snippet' % 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
"""
code = node.rawsource.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' % (
# Some filenames have '_', which is special in latex.
fname.replace('_', r'\_'),
)
)
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 ''))
# Prevent rawsource from appearing in output a second time.
raise nodes.SkipNode
def depart_snippet_latex(self, node):
"""
Latex document generator depart handler.
"""
pass
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 = '\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
required_arguments = 1

View File

@ -29,15 +29,14 @@ pre {
}
/* Header for some code blocks. */
.snippet-filename {
.code-block-caption {
background-color: #393939;
color: white;
margin: 0;
padding: 0.5em;
font: bold 90% monospace;
}
.snippet-filename + .highlight > pre,
.snippet-filename + pre {
.literal-block-wrapper pre {
margin-top: 0;
}

View File

@ -101,9 +101,8 @@ 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; }
div.code-block-caption { color: white; background-color: #234F32; margin: 0; padding: 2px 5px; width: 100%; font-family: monospace; font-size: small; line-height: 1.3em; }
div.literal-block-wrapper 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; }

View File

@ -58,7 +58,7 @@ look like in JSON:
And here's that same fixture as YAML:
.. code-block:: none
.. code-block:: yaml
- model: myapp.person
pk: 1

View File

@ -39,8 +39,8 @@ attribute::
You can also provide hints that will be passed to the :meth:`allow_migrate()`
method of database routers as ``**hints``:
.. snippet::
:filename: myapp/dbrouters.py
.. code-block:: python
:caption: myapp/dbrouters.py
class MyRouter:
@ -97,8 +97,8 @@ the respective field according to your needs.
of the three new files) to the last migration, change ``AddField`` to
``AlterField``, and add imports of ``uuid`` and ``models``. For example:
.. snippet::
:filename: 0006_remove_uuid_null.py
.. code-block:: python
:caption: 0006_remove_uuid_null.py
# Generated by Django A.B on YYYY-MM-DD HH:MM
from django.db import migrations, models
@ -121,8 +121,8 @@ the respective field according to your needs.
* Edit the first migration file. The generated migration class should look
similar to this:
.. snippet::
:filename: 0004_add_uuid_field.py
.. code-block:: python
:caption: 0004_add_uuid_field.py
class Migration(migrations.Migration):
@ -148,8 +148,8 @@ the respective field according to your needs.
unique value (UUID in the example) for each existing row. Also add an import
of ``uuid``. For example:
.. snippet::
:filename: 0005_populate_uuid_values.py
.. code-block:: python
:caption: 0005_populate_uuid_values.py
# Generated by Django A.B on YYYY-MM-DD HH:MM
from django.db import migrations
@ -279,8 +279,8 @@ project anywhere without first installing and then uninstalling the old app.
Here's a sample migration:
.. snippet::
:filename: myapp/migrations/0124_move_old_app_to_new_app.py
.. code-block:: python
:caption: myapp/migrations/0124_move_old_app_to_new_app.py
from django.apps import apps as global_apps
from django.db import migrations

View File

@ -123,8 +123,8 @@ Imports
For example (comments are for explanatory purposes only):
.. snippet::
:filename: django/contrib/admin/example.py
.. code-block:: python
:caption: django/contrib/admin/example.py
# future
from __future__ import unicode_literals

View File

@ -446,8 +446,8 @@ Since this pattern involves a lot of boilerplate, Django provides the
:func:`~django.test.utils.isolate_apps` instances are correctly
installed, you should pass the set of targeted ``app_label`` as arguments:
.. snippet::
:filename: tests/app_label/tests.py
.. code-block:: python
:caption: tests/app_label/tests.py
from django.db import models
from django.test import SimpleTestCase

View File

@ -62,8 +62,8 @@ You'll need a few things before getting started:
* Access to Django's record on PyPI. Create a file with your credentials:
.. snippet::
:filename: ~/.pypirc
.. code-block:: ini
:caption: ~/.pypirc
[pypi]
username:YourUsername

View File

@ -25,8 +25,8 @@ The :doc:`data-model syntax </topics/db/models>` offers many rich ways of
representing your models -- so far, it's been solving many years' worth of
database-schema problems. Here's a quick example:
.. snippet::
:filename: mysite/news/models.py
.. code-block:: python
:caption: mysite/news/models.py
from django.db import models
@ -145,8 +145,8 @@ production ready :doc:`administrative interface </ref/contrib/admin/index>` --
a website that lets authenticated users add, change and delete objects. It's
as easy as registering your model in the admin site:
.. snippet::
:filename: mysite/news/models.py
.. code-block:: python
:caption: mysite/news/models.py
from django.db import models
@ -156,8 +156,8 @@ as easy as registering your model in the admin site:
content = models.TextField()
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
.. snippet::
:filename: mysite/news/admin.py
.. code-block:: python
:caption: mysite/news/admin.py
from django.contrib import admin
@ -188,8 +188,8 @@ to decouple URLs from Python code.
Here's what a URLconf might look like for the ``Reporter``/``Article``
example above:
.. snippet::
:filename: mysite/news/urls.py
.. code-block:: python
:caption: mysite/news/urls.py
from django.urls import path
@ -228,8 +228,8 @@ Generally, a view retrieves data according to the parameters, loads a template
and renders the template with the retrieved data. Here's an example view for
``year_archive`` from above:
.. snippet::
:filename: mysite/news/views.py
.. code-block:: python
:caption: mysite/news/views.py
from django.shortcuts import render
@ -257,8 +257,8 @@ in the first directory, it checks the second, and so on.
Let's say the ``news/year_archive.html`` template was found. Here's what that
might look like:
.. snippet:: html+django
:filename: mysite/news/templates/news/year_archive.html
.. code-block:: html+django
:caption: mysite/news/templates/news/year_archive.html
{% extends "base.html" %}
@ -298,8 +298,8 @@ in templates: each template has to define only what's unique to that template.
Here's what the "base.html" template, including the use of :doc:`static files
</howto/static-files/index>`, might look like:
.. snippet:: html+django
:filename: mysite/templates/base.html
.. code-block:: html+django
:caption: mysite/templates/base.html
{% load static %}
<html>

View File

@ -141,8 +141,8 @@ this. For a small app like polls, this process isn't too difficult.
3. Create a file ``django-polls/README.rst`` with the following contents:
.. snippet::
:filename: django-polls/README.rst
.. code-block:: rst
:caption: django-polls/README.rst
=====
Polls
@ -188,8 +188,8 @@ this. For a small app like polls, this process isn't too difficult.
explanation. Create a file ``django-polls/setup.py`` with the following
contents:
.. snippet::
:filename: django-polls/setup.py
.. code-block:: python
:caption: django-polls/setup.py
import os
from setuptools import find_packages, setup
@ -233,8 +233,8 @@ this. For a small app like polls, this process isn't too difficult.
file, create a file ``django-polls/MANIFEST.in`` with the following
contents:
.. snippet::
:filename: django-polls/MANIFEST.in
.. code-block:: text
:caption: django-polls/MANIFEST.in
include LICENSE
include README.rst

View File

@ -243,8 +243,8 @@ Write your first view
Let's write the first view. Open the file ``polls/views.py``
and put the following Python code in it:
.. snippet::
:filename: polls/views.py
.. code-block:: python
:caption: polls/views.py
from django.http import HttpResponse
@ -271,8 +271,8 @@ Your app directory should now look like::
In the ``polls/urls.py`` file include the following code:
.. snippet::
:filename: polls/urls.py
.. code-block:: python
:caption: polls/urls.py
from django.urls import path
@ -286,8 +286,8 @@ The next step is to point the root URLconf at the ``polls.urls`` module. In
``mysite/urls.py``, add an import for ``django.urls.include`` and insert an
:func:`~django.urls.include` in the ``urlpatterns`` list, so you have:
.. snippet::
:filename: mysite/urls.py
.. code-block:: python
:caption: mysite/urls.py
from django.contrib import admin
from django.urls import include, path

View File

@ -135,8 +135,8 @@ with a ``Question``.
These concepts are represented by simple Python classes. Edit the
:file:`polls/models.py` file so it looks like this:
.. snippet::
:filename: polls/models.py
.. code-block:: python
:caption: polls/models.py
from django.db import models
@ -211,8 +211,8 @@ is ``'polls.apps.PollsConfig'``. Edit the :file:`mysite/settings.py` file and
add that dotted path to the :setting:`INSTALLED_APPS` setting. It'll look like
this:
.. snippet::
:filename: mysite/settings.py
.. code-block:: python
:caption: mysite/settings.py
INSTALLED_APPS = [
'polls.apps.PollsConfig',
@ -423,8 +423,8 @@ representation of this object. Let's fix that by editing the ``Question`` model
:meth:`~django.db.models.Model.__str__` method to both ``Question`` and
``Choice``:
.. snippet::
:filename: polls/models.py
.. code-block:: python
:caption: polls/models.py
from django.db import models
@ -446,8 +446,8 @@ automatically-generated admin.
Note these are normal Python methods. Let's add a custom method, just for
demonstration:
.. snippet::
:filename: polls/models.py
.. code-block:: python
:caption: polls/models.py
import datetime
@ -644,8 +644,8 @@ 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:
.. snippet::
:filename: polls/admin.py
.. code-block:: python
:caption: polls/admin.py
from django.contrib import admin

View File

@ -64,8 +64,8 @@ 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:
.. snippet::
:filename: polls/views.py
.. code-block:: python
:caption: polls/views.py
def detail(request, question_id):
return HttpResponse("You're looking at question %s." % question_id)
@ -80,8 +80,8 @@ slightly different, because they take an argument:
Wire these new views into the ``polls.urls`` module by adding the following
:func:`~django.urls.path` calls:
.. snippet::
:filename: polls/urls.py
.. code-block:: python
:caption: polls/urls.py
from django.urls import path
@ -147,8 +147,8 @@ in :doc:`Tutorial 2 </intro/tutorial02>`. Here's one stab at a new ``index()``
view, which displays the latest 5 poll questions in the system, separated by
commas, according to publication date:
.. snippet::
:filename: polls/views.py
.. code-block:: python
:caption: polls/views.py
from django.http import HttpResponse
@ -196,8 +196,8 @@ Django simply as ``polls/index.html``.
Put the following code in that template:
.. snippet:: html+django
:filename: polls/templates/polls/index.html
.. code-block:: html+django
:caption: polls/templates/polls/index.html
{% if latest_question_list %}
<ul>
@ -211,8 +211,8 @@ Put the following code in that template:
Now let's update our ``index`` view in ``polls/views.py`` to use the template:
.. snippet::
:filename: polls/views.py
.. code-block:: python
:caption: polls/views.py
from django.http import HttpResponse
from django.template import loader
@ -244,8 +244,8 @@ It's a very common idiom to load a template, fill a context and return an
template. Django provides a shortcut. Here's the full ``index()`` view,
rewritten:
.. snippet::
:filename: polls/views.py
.. code-block:: python
:caption: polls/views.py
from django.shortcuts import render
@ -273,8 +273,8 @@ 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:
.. snippet::
:filename: polls/views.py
.. code-block:: python
:caption: polls/views.py
from django.http import Http404
from django.shortcuts import render
@ -295,8 +295,8 @@ 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:
.. snippet:: html+django
:filename: polls/templates/polls/detail.html
.. code-block:: html+django
:caption: polls/templates/polls/detail.html
{{ question }}
@ -309,8 +309,8 @@ 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:
.. snippet::
:filename: polls/views.py
.. code-block:: python
:caption: polls/views.py
from django.shortcuts import get_object_or_404, render
@ -351,8 +351,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:
.. snippet:: html+django
:filename: polls/templates/polls/detail.html
.. code-block:: html+django
:caption: polls/templates/polls/detail.html
<h1>{{ question.question_text }}</h1>
<ul>
@ -425,8 +425,8 @@ 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 URLconf. In the ``polls/urls.py``
file, go ahead and add an ``app_name`` to set the application namespace:
.. snippet::
:filename: polls/urls.py
.. code-block:: python
:caption: polls/urls.py
from django.urls import path
@ -442,15 +442,15 @@ file, go ahead and add an ``app_name`` to set the application namespace:
Now change your ``polls/index.html`` template from:
.. snippet:: html+django
:filename: polls/templates/polls/index.html
.. code-block:: html+django
:caption: polls/templates/polls/index.html
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
to point at the namespaced detail view:
.. snippet:: html+django
:filename: polls/templates/polls/index.html
.. code-block:: html+django
:caption: polls/templates/polls/index.html
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

View File

@ -12,8 +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 ``<form>`` element:
.. snippet:: html+django
:filename: polls/templates/polls/detail.html
.. code-block:: html+django
:caption: polls/templates/polls/detail.html
<h1>{{ question.question_text }}</h1>
@ -58,16 +58,16 @@ 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
created a URLconf for the polls application that includes this line:
.. snippet::
:filename: polls/urls.py
.. code-block:: python
:caption: polls/urls.py
path('<int:question_id>/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``:
.. snippet::
:filename: polls/views.py
.. code-block:: python
:caption: polls/views.py
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
@ -146,8 +146,8 @@ response documentation </ref/request-response>`.
After somebody votes in a question, the ``vote()`` view redirects to the results
page for the question. Let's write that view:
.. snippet::
:filename: polls/views.py
.. code-block:: python
:caption: polls/views.py
from django.shortcuts import get_object_or_404, render
@ -162,8 +162,8 @@ redundancy later.
Now, create a ``polls/results.html`` template:
.. snippet:: html+django
:filename: polls/templates/polls/results.html
.. code-block:: html+django
:caption: polls/templates/polls/results.html
<h1>{{ question.question_text }}</h1>
@ -234,8 +234,8 @@ Amend URLconf
First, open the ``polls/urls.py`` URLconf and change it like so:
.. snippet::
:filename: polls/urls.py
.. code-block:: python
:caption: polls/urls.py
from django.urls import path
@ -259,8 +259,8 @@ 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:
.. snippet::
:filename: polls/views.py
.. code-block:: python
:caption: polls/views.py
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render

View File

@ -166,8 +166,8 @@ whose name begins with ``test``.
Put the following in the ``tests.py`` file in the ``polls`` application:
.. snippet::
:filename: polls/tests.py
.. code-block:: python
:caption: polls/tests.py
import datetime
@ -248,8 +248,8 @@ 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:
.. snippet::
:filename: polls/models.py
.. code-block:: python
:caption: polls/models.py
def was_published_recently(self):
now = timezone.now()
@ -284,8 +284,8 @@ introduced another.
Add two more test methods to the same class, to test the behavior of the method
more comprehensively:
.. snippet::
:filename: polls/tests.py
.. code-block:: python
:caption: polls/tests.py
def test_was_published_recently_with_old_question(self):
"""
@ -400,8 +400,8 @@ The list of polls shows polls that aren't published yet (i.e. those that have a
In :doc:`Tutorial 4 </intro/tutorial04>` we introduced a class-based view,
based on :class:`~django.views.generic.list.ListView`:
.. snippet::
:filename: polls/views.py
.. code-block:: python
:caption: polls/views.py
class IndexView(generic.ListView):
template_name = 'polls/index.html'
@ -415,15 +415,15 @@ 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:
.. snippet::
:filename: polls/views.py
.. code-block:: python
:caption: polls/views.py
from django.utils import timezone
and then we must amend the ``get_queryset`` method like so:
.. snippet::
:filename: polls/views.py
.. code-block:: python
:caption: polls/views.py
def get_queryset(self):
"""
@ -450,16 +450,16 @@ our :djadmin:`shell` session above.
Add the following to ``polls/tests.py``:
.. snippet::
:filename: polls/tests.py
.. code-block:: python
:caption: polls/tests.py
from django.urls import reverse
and we'll create a shortcut function to create questions as well as a new test
class:
.. snippet::
:filename: polls/tests.py
.. code-block:: python
:caption: polls/tests.py
def create_question(question_text, days):
"""
@ -559,8 +559,8 @@ 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``:
.. snippet::
:filename: polls/views.py
.. code-block:: python
:caption: polls/views.py
class DetailView(generic.DetailView):
...
@ -574,8 +574,8 @@ 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:
.. snippet::
:filename: polls/tests.py
.. code-block:: python
:caption: polls/tests.py
class QuestionDetailViewTests(TestCase):
def test_future_question(self):

View File

@ -56,8 +56,8 @@ reference the path for templates.
Put the following code in that stylesheet (``polls/static/polls/style.css``):
.. snippet:: css
:filename: polls/static/polls/style.css
.. code-block:: css
:caption: polls/static/polls/style.css
li a {
color: green;
@ -65,8 +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``:
.. snippet:: html+django
:filename: polls/templates/polls/index.html
.. code-block:: html+django
:caption: polls/templates/polls/index.html
{% load static %}
@ -88,8 +88,8 @@ called ``background.gif``. In other words, put your image in
Then, add to your stylesheet (``polls/static/polls/style.css``):
.. snippet:: css
:filename: polls/static/polls/style.css
.. code-block:: css
:caption: polls/static/polls/style.css
body {
background: white url("images/background.gif") no-repeat;

View File

@ -18,8 +18,8 @@ Django the options you want when you register the object.
Let's see how this works by reordering the fields on the edit form. Replace
the ``admin.site.register(Question)`` line with:
.. snippet::
:filename: polls/admin.py
.. code-block:: python
:caption: polls/admin.py
from django.contrib import admin
@ -47,8 +47,8 @@ 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:
.. snippet::
:filename: polls/admin.py
.. code-block:: python
:caption: polls/admin.py
from django.contrib import admin
@ -81,8 +81,8 @@ 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:
.. snippet::
:filename: polls/admin.py
.. code-block:: python
:caption: polls/admin.py
from django.contrib import admin
@ -115,8 +115,8 @@ It'd be better if you could add a bunch of Choices directly when you create the
Remove the ``register()`` call for the ``Choice`` model. Then, edit the ``Question``
registration code to read:
.. snippet::
:filename: polls/admin.py
.. code-block:: python
:caption: polls/admin.py
from django.contrib import admin
@ -162,8 +162,8 @@ 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:
.. snippet::
:filename: polls/admin.py
.. code-block:: python
:caption: polls/admin.py
class ChoiceInline(admin.TabularInline):
#...
@ -194,8 +194,8 @@ more helpful if we could display individual fields. To do that, use the
tuple of field names to display, as columns, on the change list page for the
object:
.. snippet::
:filename: polls/admin.py
.. code-block:: python
:caption: polls/admin.py
class QuestionAdmin(admin.ModelAdmin):
# ...
@ -204,8 +204,8 @@ object:
Just for good measure, let's also include the ``was_published_recently()``
method from :doc:`Tutorial 2 </intro/tutorial02>`:
.. snippet::
:filename: polls/admin.py
.. code-block:: python
:caption: polls/admin.py
class QuestionAdmin(admin.ModelAdmin):
# ...
@ -226,8 +226,8 @@ representation of the output.
You can improve that by giving that method (in :file:`polls/models.py`) a few
attributes, as follows:
.. snippet::
:filename: polls/models.py
.. code-block:: python
:caption: polls/models.py
class Question(models.Model):
# ...
@ -301,8 +301,8 @@ 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:`DIRS <TEMPLATES-DIRS>` option in the :setting:`TEMPLATES` setting:
.. snippet::
:filename: mysite/settings.py
.. code-block:: python
:caption: mysite/settings.py
TEMPLATES = [
{

View File

@ -2929,8 +2929,8 @@ instantiate any other Python class) and register your models and
``ModelAdmin`` subclasses with it instead of with the default site. Finally,
update :file:`myproject/urls.py` to reference your :class:`AdminSite` subclass.
.. snippet::
:filename: myapp/admin.py
.. code-block:: python
:caption: myapp/admin.py
from django.contrib.admin import AdminSite
@ -2943,8 +2943,8 @@ update :file:`myproject/urls.py` to reference your :class:`AdminSite` subclass.
admin_site.register(MyModel)
.. snippet::
:filename: myproject/urls.py
.. code-block:: python
:caption: myproject/urls.py
from django.urls import path
@ -2972,24 +2972,24 @@ You can override the default ``django.contrib.admin.site`` by setting the
to the dotted import path of either a ``AdminSite`` subclass or a callable that
returns a site instance.
.. snippet::
:filename: myproject/admin.py
.. code-block:: python
:caption: myproject/admin.py
from django.contrib import admin
class MyAdminSite(admin.AdminSite):
...
.. snippet::
:filename: myproject/apps.py
.. code-block:: python
:caption: myproject/apps.py
from django.contrib.admin.apps import AdminConfig
class MyAdminConfig(AdminConfig):
default_site = 'myproject.admin.MyAdminSite'
.. snippet::
:filename: myproject/settings.py
.. code-block:: python
:caption: myproject/settings.py
INSTALLED_APPS = [
...

View File

@ -31,8 +31,8 @@ In your custom ``change_form.html`` template, extend the
<script type="text/javascript" src="{% static 'app/formset_handlers.js' %}"></script>
{% endblock %}
.. snippet:: javascript
:filename: app/static/app/formset_handlers.js
.. code-block:: javascript
:caption: app/static/app/formset_handlers.js
(function($) {
$(document).on('formset:added', function(event, $row, formsetName) {
@ -69,8 +69,8 @@ namespace, just listen to the event triggered from there. For example:
<script type="text/javascript" src="{% static 'app/unregistered_handlers.js' %}"></script>
{% endblock %}
.. snippet:: javascript
:filename: app/static/app/unregistered_handlers.js
.. code-block:: javascript
:caption: app/static/app/unregistered_handlers.js
django.jQuery(document).on('formset:added', function(event, $row, formsetName) {
// Row added

View File

@ -314,8 +314,8 @@ The ``Func`` API is as follows:
``arg_joiner``, and any other ``**extra_context`` parameters to
customize the SQL as needed. For example:
.. snippet::
:filename: django/db/models/functions.py
.. code-block:: python
:caption: django/db/models/functions.py
class ConcatPair(Func):
...

View File

@ -1194,8 +1194,8 @@ Relationships defined this way on :ref:`abstract models
<abstract-base-classes>` are resolved when the model is subclassed as a
concrete model and are not relative to the abstract model's ``app_label``:
.. snippet::
:filename: products/models.py
.. code-block:: python
:caption: products/models.py
from django.db import models
@ -1205,8 +1205,8 @@ concrete model and are not relative to the abstract model's ``app_label``:
class Meta:
abstract = True
.. snippet::
:filename: production/models.py
.. code-block:: python
:caption: production/models.py
from django.db import models
from products.models import AbstractCar

View File

@ -559,8 +559,8 @@ The auto-escaping tag passes its effect onto templates that extend the
current one as well as templates included via the :ttag:`include` tag,
just like all block tags. For example:
.. snippet::
:filename: base.html
.. code-block:: html+django
:caption: base.html
{% autoescape off %}
<h1>{% block title %}{% endblock %}</h1>
@ -568,8 +568,8 @@ just like all block tags. For example:
{% endblock %}
{% endautoescape %}
.. snippet::
:filename: child.html
.. code-block:: html+django
:caption: child.html
{% extends "base.html" %}
{% block title %}This &amp; that{% endblock %}
@ -649,15 +649,15 @@ of all comments related to the current task with::
And of course you can easily access methods you've explicitly defined on your
own models:
.. snippet::
:filename: models.py
.. code-block:: python
:caption: models.py
class Task(models.Model):
def foo(self):
return "bar"
.. snippet::
:filename: template.html
.. code-block:: html+django
:caption: template.html
{{ task.foo }}

View File

@ -1264,8 +1264,8 @@ attribute (as below). If the ``app_name`` is set in this new way, the
``namespace`` argument is no longer required. It will default to the value of
``app_name``. For example, the URL patterns in the tutorial are changed from:
.. snippet::
:filename: mysite/urls.py
.. code-block:: python
:caption: mysite/urls.py
urlpatterns = [
url(r'^polls/', include('polls.urls', namespace="polls")),
@ -1274,16 +1274,16 @@ attribute (as below). If the ``app_name`` is set in this new way, the
to:
.. snippet::
:filename: mysite/urls.py
.. code-block:: python
:caption: mysite/urls.py
urlpatterns = [
url(r'^polls/', include('polls.urls')), # 'namespace="polls"' removed
...
]
.. snippet::
:filename: polls/urls.py
.. code-block:: python
:caption: polls/urls.py
app_name = 'polls' # added
urlpatterns = [...]
@ -1292,8 +1292,8 @@ This change also means that the old way of including an ``AdminSite`` instance
is deprecated. Instead, pass ``admin.site.urls`` directly to
:func:`~django.conf.urls.url()`:
.. snippet::
:filename: urls.py
.. code-block:: python
:caption: urls.py
from django.conf.urls import url
from django.contrib import admin

View File

@ -251,8 +251,8 @@ modify the pattern to work with any algorithm or with a custom user model.
First, we'll add the custom hasher:
.. snippet::
:filename: accounts/hashers.py
.. code-block:: python
:caption: accounts/hashers.py
from django.contrib.auth.hashers import (
PBKDF2PasswordHasher, SHA1PasswordHasher,
@ -271,8 +271,8 @@ First, we'll add the custom hasher:
The data migration might look something like:
.. snippet::
:filename: accounts/migrations/0002_migrate_sha1_passwords.py
.. code-block:: python
:caption: accounts/migrations/0002_migrate_sha1_passwords.py
from django.db import migrations
@ -306,8 +306,8 @@ several thousand users, depending on the speed of your hardware.
Finally, we'll add a :setting:`PASSWORD_HASHERS` setting:
.. snippet::
:filename: mysite/settings.py
.. code-block:: python
:caption: mysite/settings.py
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher',

View File

@ -18,8 +18,8 @@ Basic forms
Given a simple contact form:
.. snippet::
:filename: forms.py
.. code-block:: python
:caption: forms.py
from django import forms
@ -33,8 +33,8 @@ Given a simple contact form:
The view can be constructed using a ``FormView``:
.. snippet::
:filename: views.py
.. code-block:: python
:caption: views.py
from myapp.forms import ContactForm
from django.views.generic.edit import FormView
@ -96,8 +96,8 @@ add extra validation) simply set
First we need to add :meth:`~django.db.models.Model.get_absolute_url()` to our
``Author`` class:
.. snippet::
:filename: models.py
.. code-block:: python
:caption: models.py
from django.db import models
from django.urls import reverse
@ -112,8 +112,8 @@ Then we can use :class:`CreateView` and friends to do the actual
work. Notice how we're just configuring the generic class-based views
here; we don't have to write any logic ourselves:
.. snippet::
:filename: views.py
.. code-block:: python
:caption: views.py
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView, DeleteView, UpdateView
@ -146,8 +146,8 @@ and :attr:`~django.views.generic.edit.FormMixin.form_class` attributes, an
Finally, we hook these new views into the URLconf:
.. snippet::
:filename: urls.py
.. code-block:: python
:caption: urls.py
from django.urls import path
from myapp.views import AuthorCreate, AuthorDelete, AuthorUpdate
@ -187,8 +187,8 @@ To track the user that created an object using a :class:`CreateView`,
you can use a custom :class:`~django.forms.ModelForm` to do this. First, add
the foreign key relation to the model:
.. snippet::
:filename: models.py
.. code-block:: python
:caption: models.py
from django.contrib.auth.models import User
from django.db import models
@ -203,8 +203,8 @@ In the view, ensure that you don't include ``created_by`` in the list of fields
to edit, and override
:meth:`~django.views.generic.edit.ModelFormMixin.form_valid()` to add the user:
.. snippet::
:filename: views.py
.. code-block:: python
:caption: views.py
from django.views.generic.edit import CreateView
from myapp.models import Author

View File

@ -222,8 +222,8 @@ we'll want the functionality provided by
We'll demonstrate this with the ``Author`` model we used in the
:doc:`generic class-based views introduction<generic-display>`.
.. snippet::
:filename: views.py
.. code-block:: python
:caption: views.py
from django.http import HttpResponseForbidden, HttpResponseRedirect
from django.urls import reverse
@ -255,8 +255,8 @@ mixin.
We can hook this into our URLs easily enough:
.. snippet::
:filename: urls.py
.. code-block:: python
:caption: urls.py
from django.urls import path
from books.views import RecordInterest

View File

@ -1429,8 +1429,8 @@ store your models. You must import the models in the ``__init__.py`` file.
For example, if you had ``organic.py`` and ``synthetic.py`` in the ``models``
directory:
.. snippet::
:filename: myapp/models/__init__.py
.. code-block:: python
:caption: myapp/models/__init__.py
from .organic import Person
from .synthetic import Robot

View File

@ -226,8 +226,8 @@ The :class:`Form` class
We already know what we want our HTML form to look like. Our starting point for
it in Django is this:
.. snippet::
:filename: forms.py
.. code-block:: python
:caption: forms.py
from django import forms
@ -276,8 +276,8 @@ logic.
To handle the form we need to instantiate it in the view for the URL where we
want it to be published:
.. snippet::
:filename: views.py
.. code-block:: python
:caption: views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
@ -404,8 +404,8 @@ More on fields
Consider a more useful form than our minimal example above, which we could use
to implement "contact me" functionality on a personal website:
.. snippet::
:filename: forms.py
.. code-block:: python
:caption: forms.py
from django import forms
@ -453,8 +453,8 @@ values to a Python ``int`` and ``float`` respectively.
Here's how the form data could be processed in the view that handles this form:
.. snippet::
:filename: views.py
.. code-block:: python
:caption: views.py
from django.core.mail import send_mail

View File

@ -21,8 +21,8 @@ Basic file uploads
Consider a simple form containing a :class:`~django.forms.FileField`:
.. snippet::
:filename: forms.py
.. code-block:: python
:caption: forms.py
from django import forms
@ -46,8 +46,8 @@ Most of the time, you'll simply pass the file data from ``request`` into the
form as described in :ref:`binding-uploaded-files`. This would look
something like:
.. snippet::
:filename: views.py
.. code-block:: python
:caption: views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
@ -133,8 +133,8 @@ Uploading multiple files
If you want to upload multiple files using one form field, set the ``multiple``
HTML attribute of field's widget:
.. snippet::
:filename: forms.py
.. code-block:: python
:caption: forms.py
from django import forms
@ -145,8 +145,8 @@ Then override the ``post`` method of your
:class:`~django.views.generic.edit.FormView` subclass to handle multiple file
uploads:
.. snippet::
:filename: views.py
.. code-block:: python
:caption: views.py
from django.views.generic.edit import FormView
from .forms import FileFieldForm

View File

@ -761,8 +761,8 @@ and one called ``'publisher-polls'``. Assume we have enhanced that application
so that it takes the instance namespace into consideration when creating and
displaying polls.
.. snippet::
:filename: urls.py
.. code-block:: python
:caption: urls.py
from django.urls import include, path
@ -771,8 +771,8 @@ displaying polls.
path('publisher-polls/', include('polls.urls', namespace='publisher-polls')),
]
.. snippet::
:filename: polls/urls.py
.. code-block:: python
:caption: polls/urls.py
from django.urls import path
@ -830,8 +830,8 @@ at the same level as the ``urlpatterns`` attribute. You have to pass the actual
module, or a string reference to the module, to :func:`~django.urls.include`,
not the list of ``urlpatterns`` itself.
.. snippet::
:filename: polls/urls.py
.. code-block:: python
:caption: polls/urls.py
from django.urls import path
@ -844,8 +844,8 @@ not the list of ``urlpatterns`` itself.
...
]
.. snippet::
:filename: urls.py
.. code-block:: python
:caption: urls.py
from django.urls import include, path

View File

@ -429,8 +429,8 @@ configuration process for :ref:`Django's default logging
<default-logging-configuration>`. Here's an example that disables Django's
logging configuration and then manually configures logging:
.. snippet::
:filename: settings.py
.. code-block:: python
:caption: settings.py
LOGGING_CONFIG = None

View File

@ -325,8 +325,8 @@ following structure::
Let's take a look inside a couple of those files:
.. snippet::
:filename: runtests.py
.. code-block:: python
:caption: runtests.py
#!/usr/bin/env python
import os
@ -353,8 +353,8 @@ necessary to use the Django test runner. You may want to add
command-line options for controlling verbosity, passing in specific test
labels to run, etc.
.. snippet::
:filename: tests/test_settings.py
.. code-block:: python
:caption: tests/test_settings.py
SECRET_KEY = 'fake-key'
INSTALLED_APPS = [