Tutorials review #688

Closed
wants to merge 3 commits into
from
@@ -50,52 +50,18 @@ 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
know.
-Completing your reusable app
-============================
+Your project and your reusable app
+==================================
After the previous tutorials, our project should look like this::
mysite/
manage.py
mysite/
__init__.py
- settings.py
- urls.py
- wsgi.py
- polls/
- admin.py
- __init__.py
- models.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
+ mytemplates/
+ admin/
+ base_site.html
settings.py
urls.py
wsgi.py
@@ -112,33 +78,17 @@ Inside the ``polls`` app, create a new ``templates`` directory. Now move the
urls.py
views.py
-Your project-wide templates directory should now look like this::
-
- mytemplates/
- admin/
- base_site.html
-
-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`.
+You created ``mysite/mytemplates`` in :doc:`Tutorial 2 </intro/tutorial02>`, and
+``polls/templates`` in :doc:`Tutorial 3 </intro/tutorial03>`. Now perhaps it is
+clearer why we chose to have separate template directories for the project and
+application: everything that is part of the polls application is in ``polls``.
+It makes the application self-contained, and easier to pick up this package and
+drop it into a new project.
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
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.
-
@evildmp

evildmp Feb 5, 2013

Contributor

Removed material is now in the earlier tutorials, which keep application and project templates separate from the start.

.. _installing-reusable-apps-prerequisites:
Installing some prerequisites
@@ -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
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
@evildmp

evildmp Jan 31, 2013

Contributor

Capitalisation is not correct for emphasis in HTML.

production environment. It's intended only for use while developing. (We're in
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):
poll = models.ForeignKey(Poll)
choice_text = models.CharField(max_length=200)
- votes = models.IntegerField()
+ votes = models.IntegerField(default=0)
@evildmp

evildmp Jan 31, 2013

Contributor

Otherwise the poll admin asks the user to enter the number of votes when creating a Choice, which is distractingly weird.

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
@@ -370,11 +370,14 @@ 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
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
:attr:`~django.db.models.CharField.max_length`. That's used not only in the
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
:class:`~django.db.models.ForeignKey`. That tells Django each ``Choice`` is related
to a single ``Poll``. Django supports all the common database relationships:
View
@@ -435,6 +435,9 @@ 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
system.
+Customizing your *project's* templates
+--------------------------------------
+
Open your settings file (``mysite/settings.py``, remember) and look at the
:setting:`TEMPLATE_DIRS` setting. :setting:`TEMPLATE_DIRS` is a tuple of
filesystem directories to check when loading Django templates. It's a search
@@ -489,11 +492,25 @@ 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
changes.
+Customising your *application's* templates
+------------------------------------------
@evildmp

evildmp Jan 31, 2013

Contributor

I think it important to make the distinction between a project's templates and application templates. In this example we have amended a project template, but since we've also mentioned application templates they deserve a little more explanation.

+
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
default, Django automatically looks for a ``templates/`` subdirectory within
-each app package, for use as a fallback. See the :ref:`template loader
-documentation <template-loaders>` for full information.
+each application package, for use as a fallback (don't forget that
+``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
+that needed a Polls application of this sort, 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
==============================
View
@@ -39,7 +39,25 @@ In our poll application, we'll have the following four views:
* Vote action -- handles voting for a particular choice in a particular
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. Django will choose a view by examining
+the URL (to be precise, the part of the URL after the domain name) that a site
+visitor provides.
+
+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=A6CD4967199A42D9B65B1B08851C402B&SiteID=
+8112722C039B4E508F0AB8552B898895". 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
=====================
@@ -52,19 +70,8 @@ and put the following Python code in it::
def index(request):
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 view get called? For that we need to map it to a URL, in Django this is
-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.
+This is the simplest view possible in Django. To call the view, we need to map
+it to a URL - and for this we need a URLconf.
To create a URLconf in the polls directory, create a file called ``urls.py``.
Your app directory should now look like::
@@ -274,10 +281,45 @@ commas, according to publication date::
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.
-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
@evildmp

evildmp Jan 31, 2013

Contributor

Explains application template namespacing.

@timgraham

timgraham Feb 3, 2013

Owner

Reuseable apps tutorial needs to be updated for this change; see the note "Why nested?"

+creating a template that the view can use.
+
+.. 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 your
+*application*, so unlike the template we created in the previous tutorial, we'll
+put this one in the application's rather than the project's templates. We'll
+discuss in more detail in the :doc:`reusable apps
+tutorial</intro/reusable-apps>` *why* we do this.
+
+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 why
+it knows how to find the polls templates even though we didn't modify
+:setting:`TEMPLATE_DIRS`, as we did in :doc:`Tutorial 2 </intro/tutorial02>` for
+``/mysite/mytemplates``.
+
+.. admonition:: Referring to templates
+
+ Now we *might* be able to get away with putting our templates directly in
+ there, but it would actually be a bad idea. This is because Django will
+ choose the first template it finds whose name matches. If you had a template
+ with the same name in a *different* application, it would be unable to
+ distinguish between them. We need to be able to point Django at the right
+ one, and the easiest way to help ensure this is by *namespacing* them. That
+ is, by putting those templates inside *another* directory, named for the
+ application itself, and letting Django know.
+
+Within the ``templates`` directory you have just created, create another called
+``polls``, and within that, create a file called ``index.html``. In other words,
+your template is at ``polls/template/polls/index.html`` and Django can refer to
+it as ``polls/template/polls/index.html``.
-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:
.. code-block:: html+django
@@ -311,15 +353,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
objects.
-Load the page in your Web browser, and you should see a bulleted-list
-containing the "What's up" poll from Tutorial 1. The link points 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>`.
+Load the page in your Web browser - point your browser at "/polls/" on your site
+- and you should see a bulleted-list containing the "What's up" poll from
+Tutorial 1. The link points to the poll's detail page.
A shortcut: :func:`~django.shortcuts.render`
--------------------------------------------
@@ -536,8 +572,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
``{% url %}`` template tag?
-The answer is to add namespaces to your root URLconf. In the
-``mysite/urls.py`` file, go ahead and change it to include namespacing::
+The answer is to add namespaces to your root URLconf. In the ``mysite/urls.py``
+file - the project's not the application's - go ahead and change it to include
+namespacing::
from django.conf.urls import patterns, include, url
View
@@ -33,7 +33,7 @@ A quick rundown:
``value`` of each radio button is the associated poll choice's ID. The
``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
- 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
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.
+Amend URLconf
+-------------
+
First, open the ``polls/urls.py`` URLconf and change it like so::
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'),
)
+Amend views
+-----------
+
We're using two generic views here:
:class:`~django.views.generic.list.ListView` and
: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
tell Django to use the variable you want.
-You can now delete the ``index()``, ``detail()`` and ``results()``
-views from ``polls/views.py``. We don't need them anymore -- they have
-been replaced by generic views.
+You can now delete the ``index()``, ``detail()`` and ``results()`` views from
+``polls/views.py``. We don't need them anymore -- they have been replaced by
+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.