[1.5.x] Fixed #19706 - Tweaks to the tutorial.

Thanks Daniele Procida.

Backport of aa85ccf8ce from master.
This commit is contained in:
Tim Graham 2013-02-07 05:51:25 -05:00
parent a81363d85a
commit ad2311dfc6
5 changed files with 115 additions and 102 deletions

View File

@ -50,8 +50,8 @@ projects and ready to publish for others to install and use.
Python package easy for others to install. It can be a little confusing, we Python package easy for others to install. It can be a little confusing, we
know. know.
Completing your reusable app Your project and your reusable app
============================ ==================================
After the previous tutorials, our project should look like this:: After the previous tutorials, our project should look like this::
@ -67,78 +67,28 @@ After the previous tutorials, our project should look like this::
__init__.py __init__.py
models.py models.py
tests.py tests.py
urls.py
views.py
You also have a directory somewhere called ``mytemplates`` which you created in
:doc:`Tutorial 2 </intro/tutorial02>`. You specified its location in the
TEMPLATE_DIRS setting. This directory should look like this::
mytemplates/
admin/
base_site.html
polls/
detail.html
index.html
results.html
The polls app is already a Python package, thanks to the ``polls/__init__.py``
file. That's a great start, but we can't just pick up this package and drop it
into a new project. The polls templates are currently stored in the
project-wide ``mytemplates`` directory. To make the app self-contained, it
should also contain the necessary templates.
Inside the ``polls`` app, create a new ``templates`` directory. Now move the
``polls`` template directory from ``mytemplates`` into the new
``templates``. Your project should now look like this::
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
wsgi.py
polls/
admin.py
__init__.py
models.py
templates/ templates/
polls/ polls/
detail.html detail.html
index.html index.html
results.html results.html
tests.py
urls.py urls.py
views.py views.py
mytemplates/
admin/
base_site.html
Your project-wide templates directory should now look like this:: You created ``mysite/mytemplates`` in :doc:`Tutorial 2 </intro/tutorial02>`,
and ``polls/templates`` in :doc:`Tutorial 3 </intro/tutorial03>`. Now perhaps
mytemplates/ it is clearer why we chose to have separate template directories for the
admin/ project and application: everything that is part of the polls application is in
base_site.html ``polls``. It makes the application self-contained and easier to drop into a
new project.
Looking good! Now would be a good time to confirm that your polls application
still works correctly. How does Django know how to find the new location of
the polls templates even though we didn't modify :setting:`TEMPLATE_DIRS`?
Django has a :setting:`TEMPLATE_LOADERS` setting which contains a list
of callables that know how to import templates from various sources. One of
the defaults is :class:`django.template.loaders.app_directories.Loader` which
looks for a "templates" subdirectory in each of the :setting:`INSTALLED_APPS`.
The ``polls`` directory could now be copied into a new Django project and The ``polls`` directory could now be copied into a new Django project and
immediately reused. It's not quite ready to be published though. For that, we immediately reused. It's not quite ready to be published though. For that, we
need to package the app to make it easy for others to install. need to package the app to make it easy for others to install.
.. admonition:: Why nested?
Why create a ``polls`` directory under ``templates`` when we're
already inside the polls app? This directory is needed to avoid conflicts in
Django's ``app_directories`` template loader. For example, if two
apps had a template called ``base.html``, without the extra directory it
wouldn't be possible to distinguish between the two. It's a good convention
to use the name of your app for this directory.
.. _installing-reusable-apps-prerequisites: .. _installing-reusable-apps-prerequisites:
Installing some prerequisites Installing some prerequisites

View File

@ -144,7 +144,7 @@ purely in Python. We've included this with Django so you can develop things
rapidly, without having to deal with configuring a production server -- such as rapidly, without having to deal with configuring a production server -- such as
Apache -- until you're ready for production. Apache -- until you're ready for production.
Now's a good time to note: DON'T use this server in anything resembling a Now's a good time to note: **Don't** use this server in anything resembling a
production environment. It's intended only for use while developing. (We're in production environment. It's intended only for use while developing. (We're in
the business of making Web frameworks, not Web servers.) the business of making Web frameworks, not Web servers.)
@ -347,7 +347,7 @@ These concepts are represented by simple Python classes. Edit the
class Choice(models.Model): class Choice(models.Model):
poll = models.ForeignKey(Poll) poll = models.ForeignKey(Poll)
choice_text = models.CharField(max_length=200) choice_text = models.CharField(max_length=200)
votes = models.IntegerField() votes = models.IntegerField(default=0)
The code is straightforward. Each model is represented by a class that The code is straightforward. Each model is represented by a class that
subclasses :class:`django.db.models.Model`. Each model has a number of class subclasses :class:`django.db.models.Model`. Each model has a number of class
@ -370,11 +370,15 @@ example, we've only defined a human-readable name for ``Poll.pub_date``. For all
other fields in this model, the field's machine-readable name will suffice as other fields in this model, the field's machine-readable name will suffice as
its human-readable name. its human-readable name.
Some :class:`~django.db.models.Field` classes have required elements. Some :class:`~django.db.models.Field` classes have required arguments.
:class:`~django.db.models.CharField`, for example, requires that you give it a :class:`~django.db.models.CharField`, for example, requires that you give it a
:attr:`~django.db.models.CharField.max_length`. That's used not only in the :attr:`~django.db.models.CharField.max_length`. That's used not only in the
database schema, but in validation, as we'll soon see. database schema, but in validation, as we'll soon see.
A :class:`~django.db.models.Field` can also have various optional arguments; in
this case, we've set the :attr:`~django.db.models.Field.default` value of
``votes`` to 0.
Finally, note a relationship is defined, using Finally, note a relationship is defined, using
:class:`~django.db.models.ForeignKey`. That tells Django each ``Choice`` is related :class:`~django.db.models.ForeignKey`. That tells Django each ``Choice`` is related
to a single ``Poll``. Django supports all the common database relationships: to a single ``Poll``. Django supports all the common database relationships:

View File

@ -435,10 +435,10 @@ That's easy to change, though, using Django's template system. The Django admin
is powered by Django itself, and its interfaces use Django's own template is powered by Django itself, and its interfaces use Django's own template
system. system.
Open your settings file (``mysite/settings.py``, remember) and look at the .. _ref-customizing-your-projects-templates:
:setting:`TEMPLATE_DIRS` setting. :setting:`TEMPLATE_DIRS` is a tuple of
filesystem directories to check when loading Django templates. It's a search Customizing your *project's* templates
path. --------------------------------------
Create a ``mytemplates`` directory in your project directory. Templates can Create a ``mytemplates`` directory in your project directory. Templates can
live anywhere on your filesystem that Django can access. (Django runs as live anywhere on your filesystem that Django can access. (Django runs as
@ -489,11 +489,24 @@ override a template, just do the same thing you did with ``base_site.html`` --
copy it from the default directory into your custom directory, and make copy it from the default directory into your custom directory, and make
changes. changes.
Customizing your *application's* templates
------------------------------------------
Astute readers will ask: But if :setting:`TEMPLATE_DIRS` was empty by default, Astute readers will ask: But if :setting:`TEMPLATE_DIRS` was empty by default,
how was Django finding the default admin templates? The answer is that, by how was Django finding the default admin templates? The answer is that, by
default, Django automatically looks for a ``templates/`` subdirectory within default, Django automatically looks for a ``templates/`` subdirectory within
each app package, for use as a fallback. See the :ref:`template loader each application package, for use as a fallback (don't forget that
documentation <template-loaders>` for full information. ``django.contrib.admin`` is an application).
Our poll application is not very complex and doesn't need custom admin
templates. But if it grew more sophisticated and required modification of
Django's standard admin templates for some of its functionality, it would be
more sensible to modify the *application's* templates, rather than those in the
*project*. That way, you could include the polls application in any new project
and be assured that it would find the custom templates it needed.
See the :ref:`template loader documentation <template-loaders>` for more
information about how Django finds its templates.
Customize the admin index page Customize the admin index page
============================== ==============================

View File

@ -39,7 +39,24 @@ In our poll application, we'll have the following four views:
* Vote action -- handles voting for a particular choice in a particular * Vote action -- handles voting for a particular choice in a particular
poll. poll.
In Django, each view is represented by a simple Python function. In Django, web pages and other content are delivered by views. Each view is
represented by a simple Python function (or method, in the case of class-based
views). Django will choose a view by examining the URL that's requested (to be
precise, the part of the URL after the domain name).
Now in your time on the web you may have come across such beauties as
"ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B".
You will be pleased to know that Django allows us much more elegant
*URL patterns* than that.
A URL pattern is simply the general form of a URL - for example:
``/newsarchive/<year>/<month>/``.
To get from a URL to a view, Django uses what are known as 'URLconfs'. A
URLconf maps URL patterns (described as regular expressions) to views.
This tutorial provides basic instruction in the use of URLconfs, and you can
refer to :mod:`django.core.urlresolvers` for more information.
Write your first view Write your first view
===================== =====================
@ -52,19 +69,8 @@ and put the following Python code in it::
def index(request): def index(request):
return HttpResponse("Hello, world. You're at the poll index.") return HttpResponse("Hello, world. You're at the poll index.")
This is the simplest view possible in Django. Now we have a problem, how does This is the simplest view possible in Django. To call the view, we need to map
this view get called? For that we need to map it to a URL, in Django this is it to a URL - and for this we need a URLconf.
done in a configuration file called a URLconf.
.. admonition:: What is a URLconf?
In Django, web pages and other content are delivered by views and
determining which view is called is done by Python modules informally
titled 'URLconfs'. These modules are pure Python code and are a simple
mapping between URL patterns (as simple regular expressions) to Python
callback functions (your views). This tutorial provides basic instruction
in their use, and you can refer to :mod:`django.core.urlresolvers` for
more information.
To create a URLconf in the polls directory, create a file called ``urls.py``. To create a URLconf in the polls directory, create a file called ``urls.py``.
Your app directory should now look like:: Your app directory should now look like::
@ -274,10 +280,48 @@ commas, according to publication date::
There's a problem here, though: the page's design is hard-coded in the view. If There's a problem here, though: the page's design is hard-coded in the view. If
you want to change the way the page looks, you'll have to edit this Python code. you want to change the way the page looks, you'll have to edit this Python code.
So let's use Django's template system to separate the design from Python. So let's use Django's template system to separate the design from Python by
creating a template that the view can use.
First, create a directory called ``templates`` in your ``polls`` directory.
Django will look for templates in there.
Django's :setting:`TEMPLATE_LOADERS` setting contains a list of callables that
know how to import templates from various sources. One of the defaults is
:class:`django.template.loaders.app_directories.Loader` which looks for a
"templates" subdirectory in each of the :setting:`INSTALLED_APPS` - this is how
Django knows to find the polls templates even though we didn't modify
:setting:`TEMPLATE_DIRS`, as we did in :ref:`Tutorial 2
<ref-customizing-your-projects-templates>`.
.. admonition:: Organizing templates
We *could* have all our templates together, in one big templates directory,
and it would work perfectly well. However, this template belongs to the
polls application, so unlike the admin template we created in the previous
tutorial, we'll put this one in the application's template directory
(``polls/templates``) rather than the project's (``mytemplates``). We'll
discuss in more detail in the :doc:`reusable apps tutorial
</intro/reusable-apps>` *why* we do this.
Within the ``templates`` directory you have just created, create another
directory called ``polls``, and within that create a file called
``index.html``. In other words, your template should be at
``polls/templates/polls/index.html``. Because of how the ``app_directories``
template loader works as described above, you can refer to this template within
Django simply as ``polls/index.html``.
.. admonition:: Template namespacing
Now we *might* be able to get away with putting our templates directly in
``polls/templates`` (rather than creating another ``polls`` subdirectory),
but it would actually be a bad idea. Django will choose the first template
it finds whose name matches, and if you had a template with the same name
in a *different* application, Django would be unable to distinguish between
them. We need to be able to point Django at the right one, and the easiest
way to ensure this is by *namespacing* them. That is, by putting those
templates inside *another* directory named for the application itself.
First, create a directory ``polls`` in your template directory you specified
in :setting:`TEMPLATE_DIRS`. Within that, create a file called ``index.html``.
Put the following code in that template: Put the following code in that template:
.. code-block:: html+django .. code-block:: html+django
@ -311,15 +355,9 @@ That code loads the template called ``polls/index.html`` and passes it a
context. The context is a dictionary mapping template variable names to Python context. The context is a dictionary mapping template variable names to Python
objects. objects.
Load the page in your Web browser, and you should see a bulleted-list Load the page by pointing your browser at "/polls/", and you should see a
containing the "What's up" poll from Tutorial 1. The link points to the poll's bulleted-list containing the "What's up" poll from Tutorial 1. The link points
detail page. to the poll's detail page.
.. admonition:: Organizing Templates
Rather than one big templates directory, you can also store templates
within each app. We'll discuss this in more detail in the :doc:`reusable
apps tutorial</intro/reusable-apps>`.
A shortcut: :func:`~django.shortcuts.render` A shortcut: :func:`~django.shortcuts.render`
-------------------------------------------- --------------------------------------------
@ -536,8 +574,9 @@ view, and so might an app on the same project that is for a blog. How does one
make it so that Django knows which app view to create for a url when using the make it so that Django knows which app view to create for a url when using the
``{% url %}`` template tag? ``{% url %}`` template tag?
The answer is to add namespaces to your root URLconf. In the The answer is to add namespaces to your root URLconf. In the ``mysite/urls.py``
``mysite/urls.py`` file, go ahead and change it to include namespacing:: file (the project's ``urls.py``, not the application's), go ahead and change
it to include namespacing::
from django.conf.urls import patterns, include, url from django.conf.urls import patterns, include, url

View File

@ -33,7 +33,7 @@ A quick rundown:
``value`` of each radio button is the associated poll choice's ID. The ``value`` of each radio button is the associated poll choice's ID. The
``name`` of each radio button is ``"choice"``. That means, when somebody ``name`` of each radio button is ``"choice"``. That means, when somebody
selects one of the radio buttons and submits the form, it'll send the selects one of the radio buttons and submits the form, it'll send the
POST data ``choice=3``. This is HTML Forms 101. POST data ``choice=3``. This is the basic concept of HTML forms.
* We set the form's ``action`` to ``{% url 'polls:vote' poll.id %}``, and we * We set the form's ``action`` to ``{% url 'polls:vote' poll.id %}``, and we
set ``method="post"``. Using ``method="post"`` (as opposed to set ``method="post"``. Using ``method="post"`` (as opposed to
@ -199,6 +199,9 @@ Read on for details.
You should know basic math before you start using a calculator. You should know basic math before you start using a calculator.
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::
from django.conf.urls import patterns, url from django.conf.urls import patterns, url
@ -225,6 +228,9 @@ First, open the ``polls/urls.py`` URLconf and change it like so::
url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote', name='vote'), url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote', name='vote'),
) )
Amend views
-----------
We're using two generic views here: We're using two generic views here:
:class:`~django.views.generic.list.ListView` and :class:`~django.views.generic.list.ListView` and
:class:`~django.views.generic.detail.DetailView`. Respectively, those :class:`~django.views.generic.detail.DetailView`. Respectively, those
@ -267,9 +273,10 @@ As an alternative approach, you could change your templates to match
the new default context variables -- but it's a lot easier to just the new default context variables -- but it's a lot easier to just
tell Django to use the variable you want. tell Django to use the variable you want.
You can now delete the ``index()``, ``detail()`` and ``results()`` You can now delete the ``index()``, ``detail()`` and ``results()`` views from
views from ``polls/views.py``. We don't need them anymore -- they have ``polls/views.py``. We don't need them anymore -- they have been replaced by
been replaced by generic views. generic views. You can also delete the import for ``HttpResponse``, which is no
longer required.
Run the server, and use your new polling app based on generic views. Run the server, and use your new polling app based on generic views.