mirror of
				https://github.com/django/django.git
				synced 2025-11-03 21:25:09 +00:00 
			
		
		
		
	This allows to make these links more resilent to changes in the target URLs. Thanks Jannis for the report and Aymeric Augustin for the patch. Fixes #16586. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16720 bcc190cf-cafb-0310-a4f2-bffc1f526a37
		
			
				
	
	
		
			545 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			545 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
=====================================
 | 
						|
Writing your first Django app, part 3
 | 
						|
=====================================
 | 
						|
 | 
						|
This tutorial begins where :doc:`Tutorial 2 </intro/tutorial02>` left off. We're
 | 
						|
continuing the Web-poll application and will focus on creating the public
 | 
						|
interface -- "views."
 | 
						|
 | 
						|
Philosophy
 | 
						|
==========
 | 
						|
 | 
						|
A view is a "type" of Web page in your Django application that generally serves
 | 
						|
a specific function and has a specific template. For example, in a Weblog
 | 
						|
application, you might have the following views:
 | 
						|
 | 
						|
    * Blog homepage -- displays the latest few entries.
 | 
						|
 | 
						|
    * Entry "detail" page -- permalink page for a single entry.
 | 
						|
 | 
						|
    * Year-based archive page -- displays all months with entries in the
 | 
						|
      given year.
 | 
						|
 | 
						|
    * Month-based archive page -- displays all days with entries in the
 | 
						|
      given month.
 | 
						|
 | 
						|
    * Day-based archive page -- displays all entries in the given day.
 | 
						|
 | 
						|
    * Comment action -- handles posting comments to a given entry.
 | 
						|
 | 
						|
In our poll application, we'll have the following four views:
 | 
						|
 | 
						|
    * Poll "index" page -- displays the latest few polls.
 | 
						|
 | 
						|
    * Poll "detail" page -- displays a poll question, with no results but
 | 
						|
      with a form to vote.
 | 
						|
 | 
						|
    * Poll "results" page -- displays results for a particular poll.
 | 
						|
 | 
						|
    * Vote action -- handles voting for a particular choice in a particular
 | 
						|
      poll.
 | 
						|
 | 
						|
In Django, each view is represented by a simple Python function.
 | 
						|
 | 
						|
Design your URLs
 | 
						|
================
 | 
						|
 | 
						|
The first step of writing views is to design your URL structure. You do this by
 | 
						|
creating a Python module, called a URLconf. URLconfs are how Django associates
 | 
						|
a given URL with given Python code.
 | 
						|
 | 
						|
When a user requests a Django-powered page, the system looks at the
 | 
						|
:setting:`ROOT_URLCONF` setting, which contains a string in Python dotted
 | 
						|
syntax. Django loads that module and looks for a module-level variable called
 | 
						|
``urlpatterns``, which is a sequence of tuples in the following format::
 | 
						|
 | 
						|
    (regular expression, Python callback function [, optional dictionary])
 | 
						|
 | 
						|
Django starts at the first regular expression and makes its way down the list,
 | 
						|
comparing the requested URL against each regular expression until it finds one
 | 
						|
that matches.
 | 
						|
 | 
						|
When it finds a match, Django calls the Python callback function, with an
 | 
						|
:class:`~django.http.HttpRequest` object as the first argument, any "captured"
 | 
						|
values from the regular expression as keyword arguments, and, optionally,
 | 
						|
arbitrary keyword arguments from the dictionary (an optional third item in the
 | 
						|
tuple).
 | 
						|
 | 
						|
For more on :class:`~django.http.HttpRequest` objects, see the
 | 
						|
:doc:`/ref/request-response`. For more details on URLconfs, see the
 | 
						|
:doc:`/topics/http/urls`.
 | 
						|
 | 
						|
When you ran ``django-admin.py startproject mysite`` at the beginning of
 | 
						|
Tutorial 1, it created a default URLconf in ``mysite/urls.py``. It also
 | 
						|
automatically set your :setting:`ROOT_URLCONF` setting (in ``settings.py``) to
 | 
						|
point at that file::
 | 
						|
 | 
						|
    ROOT_URLCONF = 'mysite.urls'
 | 
						|
 | 
						|
Time for an example. Edit ``mysite/urls.py`` so it looks like this::
 | 
						|
 | 
						|
    from django.conf.urls.defaults import patterns, include, url
 | 
						|
 | 
						|
    from django.contrib import admin
 | 
						|
    admin.autodiscover()
 | 
						|
 | 
						|
    urlpatterns = patterns('',
 | 
						|
        (r'^polls/$', 'polls.views.index'),
 | 
						|
        (r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'),
 | 
						|
        (r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'),
 | 
						|
        (r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
 | 
						|
        url(r'^admin/', include(admin.site.urls)),
 | 
						|
    )
 | 
						|
 | 
						|
This is worth a review. When somebody requests a page from your Web site -- say,
 | 
						|
"/polls/23/", Django will load this Python module, because it's pointed to by
 | 
						|
the :setting:`ROOT_URLCONF` setting. It finds the variable named ``urlpatterns``
 | 
						|
and traverses the regular expressions in order. When it finds a regular
 | 
						|
expression that matches -- ``r'^polls/(?P<poll_id>\d+)/$'`` -- it loads the
 | 
						|
function ``detail()`` from ``polls/views.py``. Finally, it calls that
 | 
						|
``detail()`` function like so::
 | 
						|
 | 
						|
    detail(request=<HttpRequest object>, poll_id='23')
 | 
						|
 | 
						|
The ``poll_id='23'`` part comes from ``(?P<poll_id>\d+)``. Using parentheses
 | 
						|
around a pattern "captures" the text matched by that pattern and sends it as an
 | 
						|
argument to the view function; the ``?P<poll_id>`` defines the name that will be
 | 
						|
used to identify the matched pattern; and ``\d+`` is a regular expression to
 | 
						|
match a sequence of digits (i.e., a number).
 | 
						|
 | 
						|
Because the URL patterns are regular expressions, there really is no limit on
 | 
						|
what you can do with them. And there's no need to add URL cruft such as ``.php``
 | 
						|
-- unless you have a sick sense of humor, in which case you can do something
 | 
						|
like this::
 | 
						|
 | 
						|
    (r'^polls/latest\.php$', 'polls.views.index'),
 | 
						|
 | 
						|
But, don't do that. It's silly.
 | 
						|
 | 
						|
Note that these regular expressions do not search GET and POST parameters, or
 | 
						|
the domain name. For example, in a request to ``http://www.example.com/myapp/``,
 | 
						|
the URLconf will look for ``myapp/``. In a request to
 | 
						|
``http://www.example.com/myapp/?page=3``, the URLconf will look for ``myapp/``.
 | 
						|
 | 
						|
If you need help with regular expressions, see `Wikipedia's entry`_ and the
 | 
						|
documentation of the :mod:`re` module. Also, the O'Reilly book "Mastering
 | 
						|
Regular Expressions" by Jeffrey Friedl is fantastic.
 | 
						|
 | 
						|
Finally, a performance note: these regular expressions are compiled the first
 | 
						|
time the URLconf module is loaded. They're super fast.
 | 
						|
 | 
						|
.. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
 | 
						|
 | 
						|
Write your first view
 | 
						|
=====================
 | 
						|
 | 
						|
Well, we haven't created any views yet -- we just have the URLconf. But let's
 | 
						|
make sure Django is following the URLconf properly.
 | 
						|
 | 
						|
Fire up the Django development Web server:
 | 
						|
 | 
						|
.. code-block:: bash
 | 
						|
 | 
						|
    python manage.py runserver
 | 
						|
 | 
						|
Now go to "http://localhost:8000/polls/" on your domain in your Web browser.
 | 
						|
You should get a pleasantly-colored error page with the following message::
 | 
						|
 | 
						|
    ViewDoesNotExist at /polls/
 | 
						|
 | 
						|
    Tried index in module polls.views. Error was: 'module'
 | 
						|
    object has no attribute 'index'
 | 
						|
 | 
						|
This error happened because you haven't written a function ``index()`` in the
 | 
						|
module ``polls/views.py``.
 | 
						|
 | 
						|
Try "/polls/23/", "/polls/23/results/" and "/polls/23/vote/". The error
 | 
						|
messages tell you which view Django tried (and failed to find, because you
 | 
						|
haven't written any views yet).
 | 
						|
 | 
						|
Time to write the first view. Open the file ``polls/views.py``
 | 
						|
and put the following Python code in it::
 | 
						|
 | 
						|
    from django.http import HttpResponse
 | 
						|
 | 
						|
    def index(request):
 | 
						|
        return HttpResponse("Hello, world. You're at the poll index.")
 | 
						|
 | 
						|
This is the simplest view possible. Go to "/polls/" in your browser, and you
 | 
						|
should see your text.
 | 
						|
 | 
						|
Now lets add a few more views. These views are slightly different, because
 | 
						|
they take an argument (which, remember, is passed in from whatever was
 | 
						|
captured by the regular expression in the URLconf)::
 | 
						|
 | 
						|
    def detail(request, poll_id):
 | 
						|
        return HttpResponse("You're looking at poll %s." % poll_id)
 | 
						|
 | 
						|
    def results(request, poll_id):
 | 
						|
        return HttpResponse("You're looking at the results of poll %s." % poll_id)
 | 
						|
 | 
						|
    def vote(request, poll_id):
 | 
						|
        return HttpResponse("You're voting on poll %s." % poll_id)
 | 
						|
 | 
						|
Take a look in your browser, at "/polls/34/". It'll run the `detail()` method
 | 
						|
and display whatever ID you provide in the URL. Try "/polls/34/results/" and
 | 
						|
"/polls/34/vote/" too -- these will display the placeholder results and voting
 | 
						|
pages.
 | 
						|
 | 
						|
Write views that actually do something
 | 
						|
======================================
 | 
						|
 | 
						|
Each view is responsible for doing one of two things: Returning an
 | 
						|
:class:`~django.http.HttpResponse` object containing the content for the
 | 
						|
requested page, or raising an exception such as :exc:`~django.http.Http404`. The
 | 
						|
rest is up to you.
 | 
						|
 | 
						|
Your view can read records from a database, or not. It can use a template
 | 
						|
system such as Django's -- or a third-party Python template system -- or not.
 | 
						|
It can generate a PDF file, output XML, create a ZIP file on the fly, anything
 | 
						|
you want, using whatever Python libraries you want.
 | 
						|
 | 
						|
All Django wants is that :class:`~django.http.HttpResponse`. Or an exception.
 | 
						|
 | 
						|
Because it's convenient, let's use Django's own database API, which we covered
 | 
						|
in :doc:`Tutorial 1 </intro/tutorial01>`. Here's one stab at the ``index()``
 | 
						|
view, which displays the latest 5 poll questions in the system, separated by
 | 
						|
commas, according to publication date::
 | 
						|
 | 
						|
    from polls.models import Poll
 | 
						|
    from django.http import HttpResponse
 | 
						|
 | 
						|
    def index(request):
 | 
						|
        latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
 | 
						|
        output = ', '.join([p.question for p in latest_poll_list])
 | 
						|
        return HttpResponse(output)
 | 
						|
 | 
						|
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::
 | 
						|
 | 
						|
    from django.template import Context, loader
 | 
						|
    from polls.models import Poll
 | 
						|
    from django.http import HttpResponse
 | 
						|
 | 
						|
    def index(request):
 | 
						|
        latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
 | 
						|
        t = loader.get_template('polls/index.html')
 | 
						|
        c = Context({
 | 
						|
            'latest_poll_list': latest_poll_list,
 | 
						|
        })
 | 
						|
        return HttpResponse(t.render(c))
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
Reload the page. Now you'll see an error::
 | 
						|
 | 
						|
    TemplateDoesNotExist at /polls/
 | 
						|
    polls/index.html
 | 
						|
 | 
						|
Ah. There's no template yet. First, create a directory, somewhere on your
 | 
						|
filesystem, whose contents Django can access. (Django runs as whatever user your
 | 
						|
server runs.) Don't put them under your document root, though. You probably
 | 
						|
shouldn't make them public, just for security's sake.
 | 
						|
Then edit :setting:`TEMPLATE_DIRS` in your ``settings.py`` to tell Django where
 | 
						|
it can find templates -- just as you did in the "Customize the admin look and
 | 
						|
feel" section of Tutorial 2.
 | 
						|
 | 
						|
When you've done that, create a directory ``polls`` in your template directory.
 | 
						|
Within that, create a file called ``index.html``. Note that our
 | 
						|
``loader.get_template('polls/index.html')`` code from above maps to
 | 
						|
"[template_directory]/polls/index.html" on the filesystem.
 | 
						|
 | 
						|
Put the following code in that template:
 | 
						|
 | 
						|
.. code-block:: html+django
 | 
						|
 | 
						|
    {% if latest_poll_list %}
 | 
						|
        <ul>
 | 
						|
        {% for poll in latest_poll_list %}
 | 
						|
            <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>
 | 
						|
        {% endfor %}
 | 
						|
        </ul>
 | 
						|
    {% else %}
 | 
						|
        <p>No polls are available.</p>
 | 
						|
    {% endif %}
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
A shortcut: render_to_response()
 | 
						|
--------------------------------
 | 
						|
 | 
						|
It's a very common idiom to load a template, fill a context and return an
 | 
						|
:class:`~django.http.HttpResponse` object with the result of the rendered
 | 
						|
template. Django provides a shortcut. Here's the full ``index()`` view,
 | 
						|
rewritten::
 | 
						|
 | 
						|
    from django.shortcuts import render_to_response
 | 
						|
    from polls.models import Poll
 | 
						|
 | 
						|
    def index(request):
 | 
						|
        latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
 | 
						|
        return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
 | 
						|
 | 
						|
Note that once we've done this in all these views, we no longer need to import
 | 
						|
:mod:`~django.template.loader`, :class:`~django.template.Context` and
 | 
						|
:class:`~django.http.HttpResponse`.
 | 
						|
 | 
						|
The :func:`~django.shortcuts.render_to_response` function takes a template name
 | 
						|
as its first argument and a dictionary as its optional second argument. It
 | 
						|
returns an :class:`~django.http.HttpResponse` object of the given template
 | 
						|
rendered with the given context.
 | 
						|
 | 
						|
Raising 404
 | 
						|
===========
 | 
						|
 | 
						|
Now, let's tackle the poll detail view -- the page that displays the question
 | 
						|
for a given poll. Here's the view::
 | 
						|
 | 
						|
    from django.http import Http404
 | 
						|
    # ...
 | 
						|
    def detail(request, poll_id):
 | 
						|
        try:
 | 
						|
            p = Poll.objects.get(pk=poll_id)
 | 
						|
        except Poll.DoesNotExist:
 | 
						|
            raise Http404
 | 
						|
        return render_to_response('polls/detail.html', {'poll': p})
 | 
						|
 | 
						|
The new concept here: The view raises the :exc:`~django.http.Http404` exception
 | 
						|
if a poll with the requested ID doesn't exist.
 | 
						|
 | 
						|
We'll discuss what you could put in that ``polls/detail.html`` template a bit
 | 
						|
later, but if you'd like to quickly get the above example working, just::
 | 
						|
 | 
						|
    {{ poll }}
 | 
						|
 | 
						|
will get you started for now.
 | 
						|
 | 
						|
A shortcut: get_object_or_404()
 | 
						|
-------------------------------
 | 
						|
 | 
						|
It's a very common idiom to use :meth:`~django.db.models.QuerySet.get` and raise
 | 
						|
:exc:`~django.http.Http404` if the object doesn't exist. Django provides a
 | 
						|
shortcut. Here's the ``detail()`` view, rewritten::
 | 
						|
 | 
						|
    from django.shortcuts import render_to_response, get_object_or_404
 | 
						|
    # ...
 | 
						|
    def detail(request, poll_id):
 | 
						|
        p = get_object_or_404(Poll, pk=poll_id)
 | 
						|
        return render_to_response('polls/detail.html', {'poll': p})
 | 
						|
 | 
						|
The :func:`~django.shortcuts.get_object_or_404` function takes a Django model
 | 
						|
as its first argument and an arbitrary number of keyword arguments, which it
 | 
						|
passes to the module's :meth:`~django.db.models.QuerySet.get` function. It
 | 
						|
raises :exc:`~django.http.Http404` if the object doesn't exist.
 | 
						|
 | 
						|
.. admonition:: Philosophy
 | 
						|
 | 
						|
    Why do we use a helper function :func:`~django.shortcuts.get_object_or_404`
 | 
						|
    instead of automatically catching the
 | 
						|
    :exc:`~django.core.exceptions.ObjectDoesNotExist` exceptions at a higher
 | 
						|
    level, or having the model API raise :exc:`~django.http.Http404` instead of
 | 
						|
    :exc:`~django.core.exceptions.ObjectDoesNotExist`?
 | 
						|
 | 
						|
    Because that would couple the model layer to the view layer. One of the
 | 
						|
    foremost design goals of Django is to maintain loose coupling.
 | 
						|
 | 
						|
There's also a :func:`~django.shortcuts.get_list_or_404` function, which works
 | 
						|
just as :func:`~django.shortcuts.get_object_or_404` -- except using
 | 
						|
:meth:`~django.db.models.QuerySet.filter` instead of
 | 
						|
:meth:`~django.db.models.QuerySet.get`. It raises :exc:`~django.http.Http404` if
 | 
						|
the list is empty.
 | 
						|
 | 
						|
Write a 404 (page not found) view
 | 
						|
=================================
 | 
						|
 | 
						|
When you raise :exc:`~django.http.Http404` from within a view, Django will load
 | 
						|
a special view devoted to handling 404 errors. It finds it by looking for the
 | 
						|
variable ``handler404``, which is a string in Python dotted syntax -- the same
 | 
						|
format the normal URLconf callbacks use. A 404 view itself has nothing special:
 | 
						|
It's just a normal view.
 | 
						|
 | 
						|
You normally won't have to bother with writing 404 views. By default, URLconfs
 | 
						|
have the following line up top::
 | 
						|
 | 
						|
    from django.conf.urls.defaults import patterns, include, url
 | 
						|
 | 
						|
That takes care of setting ``handler404`` in the current module. As you can see
 | 
						|
in ``django/conf/urls/defaults.py``, ``handler404`` is set to
 | 
						|
:func:`django.views.defaults.page_not_found` by default.
 | 
						|
 | 
						|
Four more things to note about 404 views:
 | 
						|
 | 
						|
    * If :setting:`DEBUG` is set to ``True`` (in your settings module) then your
 | 
						|
      404 view will never be used (and thus the ``404.html`` template will never
 | 
						|
      be rendered) because the traceback will be displayed instead.
 | 
						|
 | 
						|
    * The 404 view is also called if Django doesn't find a match after checking
 | 
						|
      every regular expression in the URLconf.
 | 
						|
 | 
						|
    * If you don't define your own 404 view -- and simply use the default, which
 | 
						|
      is recommended -- you still have one obligation: To create a ``404.html``
 | 
						|
      template in the root of your template directory. The default 404 view will
 | 
						|
      use that template for all 404 errors.
 | 
						|
 | 
						|
    * If :setting:`DEBUG` is set to ``False`` (in your settings module) and if
 | 
						|
      you didn't create a ``404.html`` file, an ``Http500`` is raised instead.
 | 
						|
      So remember to create a ``404.html``.
 | 
						|
 | 
						|
Write a 500 (server error) view
 | 
						|
===============================
 | 
						|
 | 
						|
Similarly, URLconfs may define a ``handler500``, which points to a view to call
 | 
						|
in case of server errors. Server errors happen when you have runtime errors in
 | 
						|
view code.
 | 
						|
 | 
						|
Use the template system
 | 
						|
=======================
 | 
						|
 | 
						|
Back to the ``detail()`` view for our poll application. Given the context
 | 
						|
variable ``poll``, here's what the "polls/detail.html" template might look
 | 
						|
like:
 | 
						|
 | 
						|
.. code-block:: html+django
 | 
						|
 | 
						|
    <h1>{{ poll.question }}</h1>
 | 
						|
    <ul>
 | 
						|
    {% for choice in poll.choice_set.all %}
 | 
						|
        <li>{{ choice.choice }}</li>
 | 
						|
    {% endfor %}
 | 
						|
    </ul>
 | 
						|
 | 
						|
The template system uses dot-lookup syntax to access variable attributes. In
 | 
						|
the example of ``{{ poll.question }}``, first Django does a dictionary lookup
 | 
						|
on the object ``poll``. Failing that, it tries an attribute lookup -- which
 | 
						|
works, in this case. If attribute lookup had failed, it would've tried a
 | 
						|
list-index lookup.
 | 
						|
 | 
						|
Method-calling happens in the ``{% for %}`` loop: ``poll.choice_set.all`` is
 | 
						|
interpreted as the Python code ``poll.choice_set.all()``, which returns an
 | 
						|
iterable of Choice objects and is suitable for use in the ``{% for %}`` tag.
 | 
						|
 | 
						|
See the :doc:`template guide </topics/templates>` for more about templates.
 | 
						|
 | 
						|
Simplifying the URLconfs
 | 
						|
========================
 | 
						|
 | 
						|
Take some time to play around with the views and template system. As you edit
 | 
						|
the URLconf, you may notice there's a fair bit of redundancy in it::
 | 
						|
 | 
						|
    urlpatterns = patterns('',
 | 
						|
        (r'^polls/$', 'polls.views.index'),
 | 
						|
        (r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'),
 | 
						|
        (r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'),
 | 
						|
        (r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
 | 
						|
    )
 | 
						|
 | 
						|
Namely, ``polls.views`` is in every callback.
 | 
						|
 | 
						|
Because this is a common case, the URLconf framework provides a shortcut for
 | 
						|
common prefixes. You can factor out the common prefixes and add them as the
 | 
						|
first argument to :func:`~django.conf.urls.defaults.patterns`, like so::
 | 
						|
 | 
						|
    urlpatterns = patterns('polls.views',
 | 
						|
        (r'^polls/$', 'index'),
 | 
						|
        (r'^polls/(?P<poll_id>\d+)/$', 'detail'),
 | 
						|
        (r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
 | 
						|
        (r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
 | 
						|
    )
 | 
						|
 | 
						|
This is functionally identical to the previous formatting. It's just a bit
 | 
						|
tidier.
 | 
						|
 | 
						|
Since you generally don't want the prefix for one app to be applied to every
 | 
						|
callback in your URLconf, you can concatenate multiple
 | 
						|
:func:`~django.conf.urls.defaults.patterns`. Your full ``mysite/urls.py`` might
 | 
						|
now look like this::
 | 
						|
 | 
						|
    from django.conf.urls.defaults import patterns, include, url
 | 
						|
 | 
						|
    from django.contrib import admin
 | 
						|
    admin.autodiscover()
 | 
						|
    
 | 
						|
    urlpatterns = patterns('polls.views',
 | 
						|
        (r'^polls/$', 'index'),
 | 
						|
        (r'^polls/(?P<poll_id>\d+)/$', 'detail'),
 | 
						|
        (r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
 | 
						|
        (r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
 | 
						|
    )
 | 
						|
    
 | 
						|
    urlpatterns += patterns('',
 | 
						|
        url(r'^admin/', include(admin.site.urls)),
 | 
						|
    )
 | 
						|
 | 
						|
Decoupling the URLconfs
 | 
						|
=======================
 | 
						|
 | 
						|
While we're at it, we should take the time to decouple our poll-app URLs from
 | 
						|
our Django project configuration. Django apps are meant to be pluggable -- that
 | 
						|
is, each particular app should be transferable to another Django installation
 | 
						|
with minimal fuss.
 | 
						|
 | 
						|
Our poll app is pretty decoupled at this point, thanks to the strict directory
 | 
						|
structure that ``python manage.py startapp`` created, but one part of it is
 | 
						|
coupled to the Django settings: The URLconf.
 | 
						|
 | 
						|
We've been editing the URLs in ``mysite/urls.py``, but the URL design of an
 | 
						|
app is specific to the app, not to the Django installation -- so let's move the
 | 
						|
URLs within the app directory.
 | 
						|
 | 
						|
Copy the file ``mysite/urls.py`` to ``polls/urls.py``. Then, change
 | 
						|
``mysite/urls.py`` to remove the poll-specific URLs and insert an
 | 
						|
:func:`~django.conf.urls.defaults.include`, leaving you with::
 | 
						|
 | 
						|
    # This also imports the include function
 | 
						|
    from django.conf.urls.defaults import patterns, include, url
 | 
						|
    
 | 
						|
    from django.contrib import admin
 | 
						|
    admin.autodiscover()
 | 
						|
    
 | 
						|
    urlpatterns = patterns('',
 | 
						|
        (r'^polls/', include('polls.urls')),
 | 
						|
        url(r'^admin/', include(admin.site.urls)),
 | 
						|
    )
 | 
						|
 | 
						|
:func:`~django.conf.urls.defaults.include` simply references another URLconf.
 | 
						|
Note that the regular expression doesn't have a ``$`` (end-of-string match
 | 
						|
character) but has the trailing slash. Whenever Django encounters
 | 
						|
:func:`~django.conf.urls.defaults.include`, it chops off whatever part of the
 | 
						|
URL matched up to that point and sends the remaining string to the included
 | 
						|
URLconf for further processing.
 | 
						|
 | 
						|
Here's what happens if a user goes to "/polls/34/" in this system:
 | 
						|
 | 
						|
    * Django will find the match at ``'^polls/'``
 | 
						|
 | 
						|
    * Then, Django will strip off the matching text (``"polls/"``) and send the
 | 
						|
      remaining text -- ``"34/"`` -- to the 'polls.urls' URLconf for
 | 
						|
      further processing.
 | 
						|
 | 
						|
Now that we've decoupled that, we need to decouple the ``polls.urls``
 | 
						|
URLconf by removing the leading "polls/" from each line, and removing the
 | 
						|
lines registering the admin site. Your ``polls/urls.py`` file should now look like
 | 
						|
this::
 | 
						|
 | 
						|
    from django.conf.urls.defaults import patterns, include, url
 | 
						|
 | 
						|
    urlpatterns = patterns('polls.views',
 | 
						|
        (r'^$', 'index'),
 | 
						|
        (r'^(?P<poll_id>\d+)/$', 'detail'),
 | 
						|
        (r'^(?P<poll_id>\d+)/results/$', 'results'),
 | 
						|
        (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
 | 
						|
    )
 | 
						|
 | 
						|
The idea behind :func:`~django.conf.urls.defaults.include` and URLconf
 | 
						|
decoupling is to make it easy to plug-and-play URLs. Now that polls are in their
 | 
						|
own URLconf, they can be placed under "/polls/", or under "/fun_polls/", or
 | 
						|
under "/content/polls/", or any other path root, and the app will still work.
 | 
						|
 | 
						|
All the poll app cares about is its relative path, not its absolute path.
 | 
						|
 | 
						|
When you're comfortable with writing views, read :doc:`part 4 of this tutorial
 | 
						|
</intro/tutorial04>` to learn about simple form processing and generic views.
 |