Ticket #6286: 6286.patch

File 6286.patch, 67.1 KB (added by John DeRosa, 16 years ago)
  • docs/tutorial03.txt

     
    1 =====================================
    2 Writing your first Django app, part 3
    3 =====================================
    4 
    5 This tutorial begins where `Tutorial 2`_ left off. We're continuing the Web-poll
    6 application and will focus on creating the public interface -- "views."
    7 
    8 .. _Tutorial 2: ../tutorial02/
    9 
    10 Philosophy
    11 ==========
    12 
    13 A view is a "type" of Web page in your Django application that generally serves
    14 a specific function and has a specific template. For example, in a weblog
    15 application, you might have the following views:
    16 
    17     * Blog homepage -- displays the latest few entries.
    18     * Entry "detail" page -- permalink page for a single entry.
    19     * Year-based archive page -- displays all months with entries in the
    20       given year.
    21     * Month-based archive page -- displays all days with entries in the
    22       given month.
    23     * Day-based archive page -- displays all entries in the given day.
    24     * Comment action -- handles posting comments to a given entry.
    25 
    26 In our poll application, we'll have the following four views:
    27 
    28     * Poll "archive" page -- displays the latest few polls.
    29     * Poll "detail" page -- displays a poll question, with no results but
    30       with a form to vote.
    31     * Poll "results" page -- displays results for a particular poll.
    32     * Vote action -- handles voting for a particular choice in a particular
    33       poll.
    34 
    35 In Django, each view is represented by a simple Python function.
    36 
    37 Design your URLs
    38 ================
    39 
    40 The first step of writing views is to design your URL structure. You do this by
    41 creating a Python module, called a URLconf. URLconfs are how Django associates
    42 a given URL with given Python code.
    43 
    44 When a user requests a Django-powered page, the system looks at the
    45 ``ROOT_URLCONF`` setting, which contains a string in Python dotted syntax.
    46 Django loads that module and looks for a module-level variable called
    47 ``urlpatterns``, which is a sequence of tuples in the following format::
    48 
    49     (regular expression, Python callback function [, optional dictionary])
    50 
    51 Django starts at the first regular expression and makes its way down the list,
    52 comparing the requested URL against each regular expression until it finds one
    53 that matches.
    54 
    55 When it finds a match, Django calls the Python callback function, with an
    56 ``HTTPRequest`` object as the first argument, any "captured" values from the
    57 regular expression as keyword arguments, and, optionally, arbitrary keyword
    58 arguments from the dictionary (an optional third item in the tuple).
    59 
    60 For more on ``HTTPRequest`` objects, see the `request and response documentation`_.
    61 For more details on URLconfs, see the `URLconf documentation`_.
    62 
    63 When you ran ``python django-admin.py startproject mysite`` at the beginning of
    64 Tutorial 1, it created a default URLconf in ``mysite/urls.py``. It also
    65 automatically set your ``ROOT_URLCONF`` setting (in ``settings.py``) to point
    66 at that file::
    67 
    68     ROOT_URLCONF = 'mysite.urls'
    69 
    70 Time for an example. Edit ``mysite/urls.py`` so it looks like this::
    71 
    72     from django.conf.urls.defaults import *
    73 
    74     urlpatterns = patterns('',
    75         (r'^polls/$', 'mysite.polls.views.index'),
    76         (r'^polls/(?P<poll_id>\d+)/$', 'mysite.polls.views.detail'),
    77         (r'^polls/(?P<poll_id>\d+)/results/$', 'mysite.polls.views.results'),
    78         (r'^polls/(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
    79     )
    80 
    81 This is worth a review. When somebody requests a page from your Web site --
    82 say, "/polls/23/", Django will load this Python module, because it's pointed to
    83 by the ``ROOT_URLCONF`` setting. It finds the variable named ``urlpatterns``
    84 and traverses the regular expressions in order. When it finds a regular
    85 expression that matches -- ``r'^polls/(?P<poll_id>\d+)/$'`` -- it loads the
    86 associated Python package/module: ``mysite.polls.views.detail``. That
    87 corresponds to the function ``detail()`` in ``mysite/polls/views.py``.
    88 Finally, it calls that ``detail()`` function like so::
    89 
    90     detail(request=<HttpRequest object>, poll_id='23')
    91 
    92 The ``poll_id='23'`` part comes from ``(?P<poll_id>\d+)``. Using parenthesis around a
    93 pattern "captures" the text matched by that pattern and sends it as an argument
    94 to the view function; the ``?P<poll_id>`` defines the name that will be used to
    95 identify the matched pattern; and ``\d+`` is a regular expression to match a sequence of
    96 digits (i.e., a number).
    97 
    98 Because the URL patterns are regular expressions, there really is no limit on
    99 what you can do with them. And there's no need to add URL cruft such as
    100 ``.php`` -- unless you have a sick sense of humor, in which case you can do
    101 something like this::
    102 
    103     (r'^polls/latest\.php$', 'mysite.polls.views.index'),
    104 
    105 But, don't do that. It's silly.
    106 
    107 Note that these regular expressions do not search GET and POST parameters, or
    108 the domain name. For example, in a request to ``http://www.example.com/myapp/``,
    109 the URLconf will look for ``/myapp/``. In a request to
    110 ``http://www.example.com/myapp/?page=3``, the URLconf will look for ``/myapp/``.
    111 
    112 If you need help with regular expressions, see `Wikipedia's entry`_ and the
    113 `Python documentation`_. Also, the O'Reilly book "Mastering Regular
    114 Expressions" by Jeffrey Friedl is fantastic.
    115 
    116 Finally, a performance note: these regular expressions are compiled the first
    117 time the URLconf module is loaded. They're super fast.
    118 
    119 .. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
    120 .. _Python documentation: http://www.python.org/doc/current/lib/module-re.html
    121 .. _request and response documentation: ../request_response/
    122 .. _URLconf documentation: ../url_dispatch/
    123 
    124 Write your first view
    125 =====================
    126 
    127 Well, we haven't created any views yet -- we just have the URLconf. But let's
    128 make sure Django is following the URLconf properly.
    129 
    130 Fire up the Django development Web server::
    131 
    132     python manage.py runserver
    133 
    134 Now go to "http://localhost:8000/polls/" on your domain in your Web browser.
    135 You should get a pleasantly-colored error page with the following message::
    136 
    137     ViewDoesNotExist at /polls/
    138 
    139     Tried index in module mysite.polls.views. Error was: 'module'
    140     object has no attribute 'index'
    141 
    142 This error happened because you haven't written a function ``index()`` in the
    143 module ``mysite/polls/views.py``.
    144 
    145 Try "/polls/23/", "/polls/23/results/" and "/polls/23/vote/". The error
    146 messages tell you which view Django tried (and failed to find, because you
    147 haven't written any views yet).
    148 
    149 Time to write the first view. Open the file ``mysite/polls/views.py``
    150 and put the following Python code in it::
    151 
    152     from django.http import HttpResponse
    153 
    154     def index(request):
    155         return HttpResponse("Hello, world. You're at the poll index.")
    156 
    157 This is the simplest view possible. Go to "/polls/" in your browser, and you
    158 should see your text.
    159 
    160 Now add the following view. It's slightly different, because it takes an
    161 argument (which, remember, is passed in from whatever was captured by the
    162 regular expression in the URLconf)::
    163 
    164     def detail(request, poll_id):
    165         return HttpResponse("You're looking at poll %s." % poll_id)
    166 
    167 Take a look in your browser, at "/polls/34/". It'll display whatever ID you
    168 provide in the URL.
    169 
    170 Write views that actually do something
    171 ======================================
    172 
    173 Each view is responsible for doing one of two things: Returning an ``HttpResponse``
    174 object containing the content for the requested page, or raising an exception
    175 such as ``Http404``. The rest is up to you.
    176 
    177 Your view can read records from a database, or not. It can use a template
    178 system such as Django's -- or a third-party Python template system -- or not.
    179 It can generate a PDF file, output XML, create a ZIP file on the fly, anything
    180 you want, using whatever Python libraries you want.
    181 
    182 All Django wants is that ``HttpResponse``. Or an exception.
    183 
    184 Because it's convenient, let's use Django's own database API, which we covered
    185 in Tutorial 1. Here's one stab at the ``index()`` view, which displays the
    186 latest 5 poll questions in the system, separated by commas, according to
    187 publication date::
    188 
    189     from mysite.polls.models import Poll
    190     from django.http import HttpResponse
    191 
    192     def index(request):
    193         latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    194         output = ', '.join([p.question for p in latest_poll_list])
    195         return HttpResponse(output)
    196 
    197 There's a problem here, though: The page's design is hard-coded in the view. If
    198 you want to change the way the page looks, you'll have to edit this Python code.
    199 So let's use Django's template system to separate the design from Python::
    200 
    201     from django.template import Context, loader
    202     from mysite.polls.models import Poll
    203     from django.http import HttpResponse
    204 
    205     def index(request):
    206         latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    207         t = loader.get_template('polls/index.html')
    208         c = Context({
    209             'latest_poll_list': latest_poll_list,
    210         })
    211         return HttpResponse(t.render(c))
    212 
    213 That code loads the template called "polls/index.html" and passes it a context. The
    214 context is a dictionary mapping template variable names to Python objects.
    215 
    216 Reload the page. Now you'll see an error::
    217 
    218     TemplateDoesNotExist at /polls/
    219     polls/index.html
    220 
    221 Ah. There's no template yet. First, create a directory, somewhere on your
    222 filesystem, whose contents Django can access. (Django runs as whatever user
    223 your server runs.) Don't put them under your document root, though. You
    224 probably shouldn't make them public, just for security's sake.
    225 
    226 Then edit ``TEMPLATE_DIRS`` in your ``settings.py`` to tell Django where it can
    227 find templates -- just as you did in the "Customize the admin look and feel"
    228 section of Tutorial 2.
    229 
    230 When you've done that, create a directory ``polls`` in your template directory.
    231 Within that, create a file called ``index.html``. Note that our
    232 ``loader.get_template('polls/index.html')`` code from above maps to
    233 "[template_directory]/polls/index.html" on the filesystem.
    234 
    235 Put the following code in that template::
    236 
    237     {% if latest_poll_list %}
    238         <ul>
    239         {% for poll in latest_poll_list %}
    240             <li>{{ poll.question }}</li>
    241         {% endfor %}
    242         </ul>
    243     {% else %}
    244         <p>No polls are available.</p>
    245     {% endif %}
    246 
    247 Load the page in your Web browser, and you should see a bulleted-list
    248 containing the "What's up" poll from Tutorial 1.
    249 
    250 A shortcut: render_to_response()
    251 --------------------------------
    252 
    253 It's a very common idiom to load a template, fill a context and return an
    254 ``HttpResponse`` object with the result of the rendered template. Django
    255 provides a shortcut. Here's the full ``index()`` view, rewritten::
    256 
    257     from django.shortcuts import render_to_response
    258     from mysite.polls.models import Poll
    259 
    260     def index(request):
    261         latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    262         return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
    263 
    264 Note that once we've done this in all these views, we no longer need to import ``loader``, ``Context`` and ``HttpResponse``.
    265 
    266 The ``render_to_response()`` function takes a template name as its first
    267 argument and a dictionary as its optional second argument. It returns an
    268 ``HttpResponse`` object of the given template rendered with the given context.
    269 
    270 Raising 404
    271 ===========
    272 
    273 Now, let's tackle the poll detail view -- the page that displays the question
    274 for a given poll. Here's the view::
    275 
    276     from django.http import Http404
    277     # ...
    278     def detail(request, poll_id):
    279         try:
    280             p = Poll.objects.get(pk=poll_id)
    281         except Poll.DoesNotExist:
    282             raise Http404
    283         return render_to_response('polls/detail.html', {'poll': p})
    284 
    285 The new concept here: The view raises the ``django.http.Http404``
    286 exception if a poll with the requested ID doesn't exist.
    287 
    288 A shortcut: get_object_or_404()
    289 -------------------------------
    290 
    291 It's a very common idiom to use ``get()`` and raise ``Http404`` if the
    292 object doesn't exist. Django provides a shortcut. Here's the ``detail()`` view,
    293 rewritten::
    294 
    295     from django.shortcuts import render_to_response, get_object_or_404
    296     # ...
    297     def detail(request, poll_id):
    298         p = get_object_or_404(Poll, pk=poll_id)
    299         return render_to_response('polls/detail.html', {'poll': p})
    300 
    301 The ``get_object_or_404()`` function takes a Django model module as its first
    302 argument and an arbitrary number of keyword arguments, which it passes to the
    303 module's ``get()`` function. It raises ``Http404`` if the object doesn't
    304 exist.
    305 
    306 .. admonition:: Philosophy
    307 
    308     Why do we use a helper function ``get_object_or_404()`` instead of
    309     automatically catching the ``DoesNotExist`` exceptions at a higher level,
    310     or having the model API raise ``Http404`` instead of ``DoesNotExist``?
    311 
    312     Because that would couple the model layer to the view layer. One of the
    313     foremost design goals of Django is to maintain loose coupling.
    314 
    315 There's also a ``get_list_or_404()`` function, which works just as
    316 ``get_object_or_404()`` -- except using ``filter()`` instead of
    317 ``get()``. It raises ``Http404`` if the list is empty.
    318 
    319 Write a 404 (page not found) view
    320 =================================
    321 
    322 When you raise ``Http404`` from within a view, Django will load a special view
    323 devoted to handling 404 errors. It finds it by looking for the variable
    324 ``handler404``, which is a string in Python dotted syntax -- the same format
    325 the normal URLconf callbacks use. A 404 view itself has nothing special: It's
    326 just a normal view.
    327 
    328 You normally won't have to bother with writing 404 views. By default, URLconfs
    329 have the following line up top::
    330 
    331     from django.conf.urls.defaults import *
    332 
    333 That takes care of setting ``handler404`` in the current module. As you can see
    334 in ``django/conf/urls/defaults.py``, ``handler404`` is set to
    335 ``'django.views.defaults.page_not_found'`` by default.
    336 
    337 Three more things to note about 404 views:
    338 
    339     * The 404 view is also called if Django doesn't find a match after checking
    340       every regular expression in the URLconf.
    341     * If you don't define your own 404 view -- and simply use the default,
    342       which is recommended -- you still have one obligation: To create a
    343       ``404.html`` template in the root of your template directory. The default
    344       404 view will use that template for all 404 errors.
    345     * If ``DEBUG`` is set to ``True`` (in your settings module) then your 404
    346       view will never be used, and the traceback will be displayed instead.
    347 
    348 Write a 500 (server error) view
    349 ===============================
    350 
    351 Similarly, URLconfs may define a ``handler500``, which points to a view to call
    352 in case of server errors. Server errors happen when you have runtime errors in
    353 view code.
    354 
    355 Use the template system
    356 =======================
    357 
    358 Back to the ``detail()`` view for our poll application. Given the context
    359 variable ``poll``, here's what the "polls/detail.html" template might look
    360 like::
    361 
    362     <h1>{{ poll.question }}</h1>
    363     <ul>
    364     {% for choice in poll.choice_set.all %}
    365         <li>{{ choice.choice }}</li>
    366     {% endfor %}
    367     </ul>
    368 
    369 The template system uses dot-lookup syntax to access variable attributes. In
    370 the example of ``{{ poll.question }}``, first Django does a dictionary lookup
    371 on the object ``poll``. Failing that, it tries attribute lookup -- which works,
    372 in this case. If attribute lookup had failed, it would've tried calling the
    373 method ``question()`` on the poll object.
    374 
    375 Method-calling happens in the ``{% for %}`` loop: ``poll.choice_set.all`` is
    376 interpreted as the Python code ``poll.choice_set.all()``, which returns an
    377 iterable of Choice objects and is suitable for use in the ``{% for %}`` tag.
    378 
    379 See the `template guide`_ for full details on how templates work.
    380 
    381 .. _template guide: ../templates/
    382 
    383 Simplifying the URLconfs
    384 ========================
    385 
    386 Take some time to play around with the views and template system. As you edit
    387 the URLconf, you may notice there's a fair bit of redundancy in it::
    388 
    389     urlpatterns = patterns('',
    390         (r'^polls/$', 'mysite.polls.views.index'),
    391         (r'^polls/(?P<poll_id>\d+)/$', 'mysite.polls.views.detail'),
    392         (r'^polls/(?P<poll_id>\d+)/results/$', 'mysite.polls.views.results'),
    393         (r'^polls/(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
    394     )
    395 
    396 Namely, ``mysite.polls.views`` is in every callback.
    397 
    398 Because this is a common case, the URLconf framework provides a shortcut for
    399 common prefixes. You can factor out the common prefixes and add them as the
    400 first argument to ``patterns()``, like so::
    401 
    402     urlpatterns = patterns('mysite.polls.views',
    403         (r'^polls/$', 'index'),
    404         (r'^polls/(?P<poll_id>\d+)/$', 'detail'),
    405         (r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
    406         (r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
    407     )
    408 
    409 This is functionally identical to the previous formatting. It's just a bit
    410 tidier.
    411 
    412 Decoupling the URLconfs
    413 =======================
    414 
    415 While we're at it, we should take the time to decouple our poll-app URLs from
    416 our Django project configuration. Django apps are meant to be pluggable -- that
    417 is, each particular app should be transferrable to another Django installation
    418 with minimal fuss.
    419 
    420 Our poll app is pretty decoupled at this point, thanks to the strict directory
    421 structure that ``python manage.py startapp`` created, but one part of it is
    422 coupled to the Django settings: The URLconf.
    423 
    424 We've been editing the URLs in ``mysite/urls.py``, but the URL design of an
    425 app is specific to the app, not to the Django installation -- so let's move the
    426 URLs within the app directory.
    427 
    428 Copy the file ``mysite/urls.py`` to ``mysite/polls/urls.py``. Then,
    429 change ``mysite/urls.py`` to remove the poll-specific URLs and insert an
    430 ``include()``::
    431 
    432     (r'^polls/', include('mysite.polls.urls')),
    433 
    434 ``include()``, simply, references another URLconf. Note that the regular
    435 expression doesn't have a ``$`` (end-of-string match character) but has the
    436 trailing slash. Whenever Django encounters ``include()``, it chops off whatever
    437 part of the URL matched up to that point and sends the remaining string to the
    438 included URLconf for further processing.
    439 
    440 Here's what happens if a user goes to "/polls/34/" in this system:
    441 
    442 * Django will find the match at ``'^polls/'``
    443 * It will strip off the matching text (``"polls/"``) and send the remaining
    444   text -- ``"34/"`` -- to the 'mysite.polls.urls' urlconf for
    445   further processing.
    446 
    447 Now that we've decoupled that, we need to decouple the
    448 'mysite.polls.urls' urlconf by removing the leading "polls/" from each
    449 line::
    450 
    451     urlpatterns = patterns('mysite.polls.views',
    452         (r'^$', 'index'),
    453         (r'^(?P<poll_id>\d+)/$', 'detail'),
    454         (r'^(?P<poll_id>\d+)/results/$', 'results'),
    455         (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
    456     )
    457 
    458 The idea behind ``include()`` and URLconf decoupling is to make it easy to
    459 plug-and-play URLs. Now that polls are in their own URLconf, they can be placed
    460 under "/polls/", or under "/fun_polls/", or under "/content/polls/", or any
    461 other URL root, and the app will still work.
    462 
    463 All the poll app cares about is its relative URLs, not its absolute URLs.
    464 
    465 When you're comfortable with writing views, read `part 4 of this tutorial`_ to
    466 learn about simple form processing and generic views.
    467 
    468 .. _part 4 of this tutorial: ../tutorial04/
     1=====================================
     2Writing your first Django app, part 3
     3=====================================
     4
     5This tutorial begins where `Tutorial 2`_ left off. We're continuing the Web-poll
     6application and will focus on creating the public interface -- "views."
     7
     8.. _Tutorial 2: ../tutorial02/
     9
     10Philosophy
     11==========
     12
     13A view is a "type" of Web page in your Django application that generally serves
     14a specific function and has a specific template. For example, in a weblog
     15application, you might have the following views:
     16
     17    * Blog homepage -- displays the latest few entries.
     18    * Entry "detail" page -- permalink page for a single entry.
     19    * Year-based archive page -- displays all months with entries in the
     20      given year.
     21    * Month-based archive page -- displays all days with entries in the
     22      given month.
     23    * Day-based archive page -- displays all entries in the given day.
     24    * Comment action -- handles posting comments to a given entry.
     25
     26In our poll application, we'll have the following four views:
     27
     28    * Poll "archive" page -- displays the latest few polls.
     29    * Poll "detail" page -- displays a poll question, with no results but
     30      with a form to vote.
     31    * Poll "results" page -- displays results for a particular poll.
     32    * Vote action -- handles voting for a particular choice in a particular
     33      poll.
     34
     35In Django, each view is represented by a simple Python function.
     36
     37Design your URLs
     38================
     39
     40The first step of writing views is to design your URL structure. You do this by
     41creating a Python module, called a URLconf. URLconfs are how Django associates
     42a given URL with given Python code.
     43
     44When a user requests a Django-powered page, the system looks at the
     45``ROOT_URLCONF`` setting, which contains a string in Python dotted syntax.
     46Django loads that module and looks for a module-level variable called
     47``urlpatterns``, which is a sequence of tuples in the following format::
     48
     49    (regular expression, Python callback function [, optional dictionary])
     50
     51Django starts at the first regular expression and makes its way down the list,
     52comparing the requested URL against each regular expression until it finds one
     53that matches.
     54
     55When it finds a match, Django calls the Python callback function, with an
     56``HTTPRequest`` object as the first argument, any "captured" values from the
     57regular expression as keyword arguments, and, optionally, arbitrary keyword
     58arguments from the dictionary (an optional third item in the tuple).
     59
     60For more on ``HTTPRequest`` objects, see the `request and response documentation`_.
     61For more details on URLconfs, see the `URLconf documentation`_.
     62
     63When you ran ``python django-admin.py startproject mysite`` at the beginning of
     64Tutorial 1, it created a default URLconf in ``mysite/urls.py``. It also
     65automatically set your ``ROOT_URLCONF`` setting (in ``settings.py``) to point
     66at that file::
     67
     68    ROOT_URLCONF = 'mysite.urls'
     69
     70Time for an example. Edit ``mysite/urls.py`` so it looks like this::
     71
     72    from django.conf.urls.defaults import *
     73
     74    urlpatterns = patterns('',
     75        (r'^polls/$', 'mysite.polls.views.index'),
     76        (r'^polls/(?P<poll_id>\d+)/$', 'mysite.polls.views.detail'),
     77        (r'^polls/(?P<poll_id>\d+)/results/$', 'mysite.polls.views.results'),
     78        (r'^polls/(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
     79    )
     80
     81(This will disable the admin site.  You'll re-enable it in `part 4 of this tutorial`_.)
     82
     83This is worth a review. When somebody requests a page from your Web site --
     84say, "/polls/23/", Django will load this Python module, because it's pointed to
     85by the ``ROOT_URLCONF`` setting. It finds the variable named ``urlpatterns``
     86and traverses the regular expressions in order. When it finds a regular
     87expression that matches -- ``r'^polls/(?P<poll_id>\d+)/$'`` -- it loads the
     88associated Python package/module: ``mysite.polls.views.detail``. That
     89corresponds to the function ``detail()`` in ``mysite/polls/views.py``.
     90Finally, it calls that ``detail()`` function like so::
     91
     92    detail(request=<HttpRequest object>, poll_id='23')
     93
     94The ``poll_id='23'`` part comes from ``(?P<poll_id>\d+)``. Using parenthesis around a
     95pattern "captures" the text matched by that pattern and sends it as an argument
     96to the view function; the ``?P<poll_id>`` defines the name that will be used to
     97identify the matched pattern; and ``\d+`` is a regular expression to match a sequence of
     98digits (i.e., a number).
     99
     100Because the URL patterns are regular expressions, there really is no limit on
     101what you can do with them. And there's no need to add URL cruft such as
     102``.php`` -- unless you have a sick sense of humor, in which case you can do
     103something like this::
     104
     105    (r'^polls/latest\.php$', 'mysite.polls.views.index'),
     106
     107But, don't do that. It's silly.
     108
     109Note that these regular expressions do not search GET and POST parameters, or
     110the domain name. For example, in a request to ``http://www.example.com/myapp/``,
     111the URLconf will look for ``/myapp/``. In a request to
     112``http://www.example.com/myapp/?page=3``, the URLconf will look for ``/myapp/``.
     113
     114If you need help with regular expressions, see `Wikipedia's entry`_ and the
     115`Python documentation`_. Also, the O'Reilly book "Mastering Regular
     116Expressions" by Jeffrey Friedl is fantastic.
     117
     118Finally, a performance note: these regular expressions are compiled the first
     119time the URLconf module is loaded. They're super fast.
     120
     121.. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
     122.. _Python documentation: http://www.python.org/doc/current/lib/module-re.html
     123.. _request and response documentation: ../request_response/
     124.. _URLconf documentation: ../url_dispatch/
     125
     126Write your first view
     127=====================
     128
     129Well, we haven't created any views yet -- we just have the URLconf. But let's
     130make sure Django is following the URLconf properly.
     131
     132Fire up the Django development Web server::
     133
     134    python manage.py runserver
     135
     136Now go to "http://localhost:8000/polls/" on your domain in your Web browser.
     137You should get a pleasantly-colored error page with the following message::
     138
     139    ViewDoesNotExist at /polls/
     140
     141    Tried index in module mysite.polls.views. Error was: 'module'
     142    object has no attribute 'index'
     143
     144This error happened because you haven't written a function ``index()`` in the
     145module ``mysite/polls/views.py``.
     146
     147Try "/polls/23/", "/polls/23/results/" and "/polls/23/vote/". The error
     148messages tell you which view Django tried (and failed to find, because you
     149haven't written any views yet).
     150
     151Time to write the first view. Open the file ``mysite/polls/views.py``
     152and put the following Python code in it::
     153
     154    from django.http import HttpResponse
     155
     156    def index(request):
     157        return HttpResponse("Hello, world. You're at the poll index.")
     158
     159This is the simplest view possible. Go to "/polls/" in your browser, and you
     160should see your text.
     161
     162Now add the following view. It's slightly different, because it takes an
     163argument (which, remember, is passed in from whatever was captured by the
     164regular expression in the URLconf)::
     165
     166    def detail(request, poll_id):
     167        return HttpResponse("You're looking at poll %s." % poll_id)
     168
     169Take a look in your browser, at "/polls/34/". It'll display whatever ID you
     170provide in the URL.
     171
     172Write views that actually do something
     173======================================
     174
     175Each view is responsible for doing one of two things: Returning an ``HttpResponse``
     176object containing the content for the requested page, or raising an exception
     177such as ``Http404``. The rest is up to you.
     178
     179Your view can read records from a database, or not. It can use a template
     180system such as Django's -- or a third-party Python template system -- or not.
     181It can generate a PDF file, output XML, create a ZIP file on the fly, anything
     182you want, using whatever Python libraries you want.
     183
     184All Django wants is that ``HttpResponse``. Or an exception.
     185
     186Because it's convenient, let's use Django's own database API, which we covered
     187in Tutorial 1. Here's one stab at the ``index()`` view, which displays the
     188latest 5 poll questions in the system, separated by commas, according to
     189publication date::
     190
     191    from mysite.polls.models import Poll
     192    from django.http import HttpResponse
     193
     194    def index(request):
     195        latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
     196        output = ', '.join([p.question for p in latest_poll_list])
     197        return HttpResponse(output)
     198
     199There's a problem here, though: The page's design is hard-coded in the view. If
     200you want to change the way the page looks, you'll have to edit this Python code.
     201So let's use Django's template system to separate the design from Python::
     202
     203    from django.template import Context, loader
     204    from mysite.polls.models import Poll
     205    from django.http import HttpResponse
     206
     207    def index(request):
     208        latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
     209        t = loader.get_template('polls/index.html')
     210        c = Context({
     211            'latest_poll_list': latest_poll_list,
     212        })
     213        return HttpResponse(t.render(c))
     214
     215That code loads the template called "polls/index.html" and passes it a context. The
     216context is a dictionary mapping template variable names to Python objects.
     217
     218Reload the page. Now you'll see an error::
     219
     220    TemplateDoesNotExist at /polls/
     221    polls/index.html
     222
     223Ah. There's no template yet. First, create a directory, somewhere on your
     224filesystem, whose contents Django can access. (Django runs as whatever user
     225your server runs.) Don't put them under your document root, though. You
     226probably shouldn't make them public, just for security's sake.
     227
     228Then edit ``TEMPLATE_DIRS`` in your ``settings.py`` to tell Django where it can
     229find templates -- just as you did in the "Customize the admin look and feel"
     230section of Tutorial 2.
     231
     232When you've done that, create a directory ``polls`` in your template directory.
     233Within that, create a file called ``index.html``. Note that our
     234``loader.get_template('polls/index.html')`` code from above maps to
     235"[template_directory]/polls/index.html" on the filesystem.
     236
     237Put the following code in that template::
     238
     239    {% if latest_poll_list %}
     240        <ul>
     241        {% for poll in latest_poll_list %}
     242            <li>{{ poll.question }}</li>
     243        {% endfor %}
     244        </ul>
     245    {% else %}
     246        <p>No polls are available.</p>
     247    {% endif %}
     248
     249Load the page in your Web browser, and you should see a bulleted-list
     250containing the "What's up" poll from Tutorial 1.
     251
     252A shortcut: render_to_response()
     253--------------------------------
     254
     255It's a very common idiom to load a template, fill a context and return an
     256``HttpResponse`` object with the result of the rendered template. Django
     257provides a shortcut. Here's the full ``index()`` view, rewritten::
     258
     259    from django.shortcuts import render_to_response
     260    from mysite.polls.models import Poll
     261
     262    def index(request):
     263        latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
     264        return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
     265
     266Note that once we've done this in all these views, we no longer need to import ``loader``, ``Context`` and ``HttpResponse``.
     267
     268The ``render_to_response()`` function takes a template name as its first
     269argument and a dictionary as its optional second argument. It returns an
     270``HttpResponse`` object of the given template rendered with the given context.
     271
     272Raising 404
     273===========
     274
     275Now, let's tackle the poll detail view -- the page that displays the question
     276for a given poll. Here's the view::
     277
     278    from django.http import Http404
     279    # ...
     280    def detail(request, poll_id):
     281        try:
     282            p = Poll.objects.get(pk=poll_id)
     283        except Poll.DoesNotExist:
     284            raise Http404
     285        return render_to_response('polls/detail.html', {'poll': p})
     286
     287The new concept here: The view raises the ``django.http.Http404``
     288exception if a poll with the requested ID doesn't exist.
     289
     290A shortcut: get_object_or_404()
     291-------------------------------
     292
     293It's a very common idiom to use ``get()`` and raise ``Http404`` if the
     294object doesn't exist. Django provides a shortcut. Here's the ``detail()`` view,
     295rewritten::
     296
     297    from django.shortcuts import render_to_response, get_object_or_404
     298    # ...
     299    def detail(request, poll_id):
     300        p = get_object_or_404(Poll, pk=poll_id)
     301        return render_to_response('polls/detail.html', {'poll': p})
     302
     303The ``get_object_or_404()`` function takes a Django model module as its first
     304argument and an arbitrary number of keyword arguments, which it passes to the
     305module's ``get()`` function. It raises ``Http404`` if the object doesn't
     306exist.
     307
     308.. admonition:: Philosophy
     309
     310    Why do we use a helper function ``get_object_or_404()`` instead of
     311    automatically catching the ``DoesNotExist`` exceptions at a higher level,
     312    or having the model API raise ``Http404`` instead of ``DoesNotExist``?
     313
     314    Because that would couple the model layer to the view layer. One of the
     315    foremost design goals of Django is to maintain loose coupling.
     316
     317There's also a ``get_list_or_404()`` function, which works just as
     318``get_object_or_404()`` -- except using ``filter()`` instead of
     319``get()``. It raises ``Http404`` if the list is empty.
     320
     321Write a 404 (page not found) view
     322=================================
     323
     324When you raise ``Http404`` from within a view, Django will load a special view
     325devoted to handling 404 errors. It finds it by looking for the variable
     326``handler404``, which is a string in Python dotted syntax -- the same format
     327the normal URLconf callbacks use. A 404 view itself has nothing special: It's
     328just a normal view.
     329
     330You normally won't have to bother with writing 404 views. By default, URLconfs
     331have the following line up top::
     332
     333    from django.conf.urls.defaults import *
     334
     335That takes care of setting ``handler404`` in the current module. As you can see
     336in ``django/conf/urls/defaults.py``, ``handler404`` is set to
     337``'django.views.defaults.page_not_found'`` by default.
     338
     339Three more things to note about 404 views:
     340
     341    * The 404 view is also called if Django doesn't find a match after checking
     342      every regular expression in the URLconf.
     343    * If you don't define your own 404 view -- and simply use the default,
     344      which is recommended -- you still have one obligation: To create a
     345      ``404.html`` template in the root of your template directory. The default
     346      404 view will use that template for all 404 errors.
     347    * If ``DEBUG`` is set to ``True`` (in your settings module) then your 404
     348      view will never be used, and the traceback will be displayed instead.
     349
     350Write a 500 (server error) view
     351===============================
     352
     353Similarly, URLconfs may define a ``handler500``, which points to a view to call
     354in case of server errors. Server errors happen when you have runtime errors in
     355view code.
     356
     357Use the template system
     358=======================
     359
     360Back to the ``detail()`` view for our poll application. Given the context
     361variable ``poll``, here's what the "polls/detail.html" template might look
     362like::
     363
     364    <h1>{{ poll.question }}</h1>
     365    <ul>
     366    {% for choice in poll.choice_set.all %}
     367        <li>{{ choice.choice }}</li>
     368    {% endfor %}
     369    </ul>
     370
     371The template system uses dot-lookup syntax to access variable attributes. In
     372the example of ``{{ poll.question }}``, first Django does a dictionary lookup
     373on the object ``poll``. Failing that, it tries attribute lookup -- which works,
     374in this case. If attribute lookup had failed, it would've tried calling the
     375method ``question()`` on the poll object.
     376
     377Method-calling happens in the ``{% for %}`` loop: ``poll.choice_set.all`` is
     378interpreted as the Python code ``poll.choice_set.all()``, which returns an
     379iterable of Choice objects and is suitable for use in the ``{% for %}`` tag.
     380
     381See the `template guide`_ for full details on how templates work.
     382
     383.. _template guide: ../templates/
     384
     385Simplifying the URLconfs
     386========================
     387
     388Take some time to play around with the views and template system. As you edit
     389the URLconf, you may notice there's a fair bit of redundancy in it::
     390
     391    urlpatterns = patterns('',
     392        (r'^polls/$', 'mysite.polls.views.index'),
     393        (r'^polls/(?P<poll_id>\d+)/$', 'mysite.polls.views.detail'),
     394        (r'^polls/(?P<poll_id>\d+)/results/$', 'mysite.polls.views.results'),
     395        (r'^polls/(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
     396    )
     397
     398Namely, ``mysite.polls.views`` is in every callback.
     399
     400Because this is a common case, the URLconf framework provides a shortcut for
     401common prefixes. You can factor out the common prefixes and add them as the
     402first argument to ``patterns()``, like so::
     403
     404    urlpatterns = patterns('mysite.polls.views',
     405        (r'^polls/$', 'index'),
     406        (r'^polls/(?P<poll_id>\d+)/$', 'detail'),
     407        (r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
     408        (r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
     409    )
     410
     411This is functionally identical to the previous formatting. It's just a bit
     412tidier.
     413
     414Decoupling the URLconfs
     415=======================
     416
     417While we're at it, we should take the time to decouple our poll-app URLs from
     418our Django project configuration. Django apps are meant to be pluggable -- that
     419is, each particular app should be transferrable to another Django installation
     420with minimal fuss.
     421
     422Our poll app is pretty decoupled at this point, thanks to the strict directory
     423structure that ``python manage.py startapp`` created, but one part of it is
     424coupled to the Django settings: The URLconf.
     425
     426We've been editing the URLs in ``mysite/urls.py``, but the URL design of an
     427app is specific to the app, not to the Django installation -- so let's move the
     428URLs within the app directory.
     429
     430Copy the file ``mysite/urls.py`` to ``mysite/polls/urls.py``. Then,
     431change ``mysite/urls.py`` to remove the poll-specific URLs and insert an
     432``include()``::
     433
     434    (r'^polls/', include('mysite.polls.urls')),
     435
     436``include()``, simply, references another URLconf. Note that the regular
     437expression doesn't have a ``$`` (end-of-string match character) but has the
     438trailing slash. Whenever Django encounters ``include()``, it chops off whatever
     439part of the URL matched up to that point and sends the remaining string to the
     440included URLconf for further processing.
     441
     442Here's what happens if a user goes to "/polls/34/" in this system:
     443
     444* Django will find the match at ``'^polls/'``
     445* It will strip off the matching text (``"polls/"``) and send the remaining
     446  text -- ``"34/"`` -- to the 'mysite.polls.urls' urlconf for
     447  further processing.
     448
     449Now that we've decoupled that, we need to decouple the
     450'mysite.polls.urls' urlconf by removing the leading "polls/" from each
     451line::
     452
     453    urlpatterns = patterns('mysite.polls.views',
     454        (r'^$', 'index'),
     455        (r'^(?P<poll_id>\d+)/$', 'detail'),
     456        (r'^(?P<poll_id>\d+)/results/$', 'results'),
     457        (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
     458    )
     459
     460The idea behind ``include()`` and URLconf decoupling is to make it easy to
     461plug-and-play URLs. Now that polls are in their own URLconf, they can be placed
     462under "/polls/", or under "/fun_polls/", or under "/content/polls/", or any
     463other URL root, and the app will still work.
     464
     465All the poll app cares about is its relative URLs, not its absolute URLs.
     466
     467When you're comfortable with writing views, read `part 4 of this tutorial`_ to
     468learn about simple form processing and generic views.
     469
     470.. _part 4 of this tutorial: ../tutorial04/
  • docs/tutorial04.txt

     
    1 =====================================
    2 Writing your first Django app, part 4
    3 =====================================
    4 
    5 This tutorial begins where `Tutorial 3`_ left off. We're continuing the Web-poll
    6 application and will focus on simple form processing and cutting down our code.
    7 
    8 Write a simple form
    9 ===================
    10 
    11 Let's update our poll detail template ("polls/detail.html") from the last
    12 tutorial, so that the template contains an HTML ``<form>`` element::
    13 
    14     <h1>{{ poll.question }}</h1>
    15 
    16     {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
    17 
    18     <form action="/polls/{{ poll.id }}/vote/" method="post">
    19     {% for choice in poll.choice_set.all %}
    20         <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
    21         <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
    22     {% endfor %}
    23     <input type="submit" value="Vote" />
    24     </form>
    25 
    26 A quick rundown:
    27 
    28     * The above template displays a radio button for each poll choice. The
    29       ``value`` of each radio button is the associated poll choice's ID. The
    30       ``name`` of each radio button is ``"choice"``. That means, when somebody
    31       selects one of the radio buttons and submits the form, it'll send the
    32       POST data ``choice=3``. This is HTML Forms 101.
    33 
    34     * We set the form's ``action`` to ``/polls/{{ poll.id }}/vote/``, and we
    35       set ``method="post"``. Using ``method="post"`` (as opposed to
    36       ``method="get"``) is very important, because the act of submitting this
    37       form will alter data server-side. Whenever you create a form that alters
    38       data server-side, use ``method="post"``. This tip isn't specific to
    39       Django; it's just good Web development practice.
    40 
    41 Now, let's create a Django view that handles the submitted data and does
    42 something with it. Remember, in `Tutorial 3`_, we created a URLconf for the
    43 polls application that includes this line::
    44 
    45     (r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
    46 
    47 So let's create a ``vote()`` function in ``mysite/polls/views.py``::
    48 
    49     from django.shortcuts import get_object_or_404, render_to_response
    50     from django.http import HttpResponseRedirect
    51     from django.core.urlresolvers import reverse
    52     from mysite.polls.models import Choice, Poll
    53     # ...
    54     def vote(request, poll_id):
    55         p = get_object_or_404(Poll, pk=poll_id)
    56         try:
    57             selected_choice = p.choice_set.get(pk=request.POST['choice'])
    58         except (KeyError, Choice.DoesNotExist):
    59             # Redisplay the poll voting form.
    60             return render_to_response('polls/detail.html', {
    61                 'poll': p,
    62                 'error_message': "You didn't select a choice.",
    63             })
    64         else:
    65             selected_choice.votes += 1
    66             selected_choice.save()
    67             # Always return an HttpResponseRedirect after successfully dealing
    68             # with POST data. This prevents data from being posted twice if a
    69             # user hits the Back button.
    70             return HttpResponseRedirect(reverse('mysite.polls.views.results', args=(p.id,)))
    71 
    72 This code includes a few things we haven't covered yet in this tutorial:
    73 
    74     * ``request.POST`` is a dictionary-like object that lets you access
    75       submitted data by key name. In this case, ``request.POST['choice']``
    76       returns the ID of the selected choice, as a string. ``request.POST``
    77       values are always strings.
    78 
    79       Note that Django also provides ``request.GET`` for accessing GET data
    80       in the same way -- but we're explicitly using ``request.POST`` in our
    81       code, to ensure that data is only altered via a POST call.
    82 
    83     * ``request.POST['choice']`` will raise ``KeyError`` if ``choice`` wasn't
    84       provided in POST data. The above code checks for ``KeyError`` and
    85       redisplays the poll form with an error message if ``choice`` isn't given.
    86 
    87     * After incrementing the choice count, the code returns an
    88       ``HttpResponseRedirect`` rather than a normal ``HttpResponse``.
    89       ``HttpResponseRedirect`` takes a single argument: the URL to which the
    90       user will be redirected (see the following point for how we construct
    91       the URL in this case).
    92 
    93       As the Python comment above points out, you should always return an
    94       ``HttpResponseRedirect`` after successfully dealing with POST data. This
    95       tip isn't specific to Django; it's just good Web development practice.
    96 
    97     * We are using the ``reverse()`` function in the ``HttpResponseRedirect``
    98       constructor in this example. This function helps avoid having to
    99       hardcode a URL in the view function. It is given the name of the view
    100       that we want to pass control to and the variable portion of the URL
    101       pattern that points to that view. In this case, using the URLConf we set
    102       up in Tutorial 3, this ``reverse()`` call will return a string like ::
    103 
    104         '/polls/3/results/'
    105 
    106       ... where the ``3`` is the value of ``p.id``. This redirected URL will
    107       then call the ``'results'`` view to display the final page. Note that
    108       you need to use the full name of the view here (including the prefix).
    109 
    110       For more information about ``reverse()``, see the `URL dispatcher`_
    111       documentation.
    112 
    113 As mentioned in Tutorial 3, ``request`` is a ``HTTPRequest`` object. For more
    114 on ``HTTPRequest`` objects, see the `request and response documentation`_.
    115 
    116 After somebody votes in a poll, the ``vote()`` view redirects to the results
    117 page for the poll. Let's write that view::
    118 
    119     def results(request, poll_id):
    120         p = get_object_or_404(Poll, pk=poll_id)
    121         return render_to_response('polls/results.html', {'poll': p})
    122 
    123 This is almost exactly the same as the ``detail()`` view from `Tutorial 3`_.
    124 The only difference is the template name. We'll fix this redundancy later.
    125 
    126 Now, create a ``results.html`` template::
    127 
    128     <h1>{{ poll.question }}</h1>
    129 
    130     <ul>
    131     {% for choice in poll.choice_set.all %}
    132         <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
    133     {% endfor %}
    134     </ul>
    135 
    136 Now, go to ``/polls/1/`` in your browser and vote in the poll. You should see a
    137 results page that gets updated each time you vote. If you submit the form
    138 without having chosen a choice, you should see the error message.
    139 
    140 .. _request and response documentation: ../request_response/
    141 .. _URL dispatcher: ../url_dispatch#reverse
    142 
    143 Use generic views: Less code is better
    144 ======================================
    145 
    146 The ``detail()`` (from `Tutorial 3`_) and ``results()`` views are stupidly
    147 simple -- and, as mentioned above, redundant. The ``index()`` view (also from
    148 Tutorial 3), which displays a list of polls, is similar.
    149 
    150 These views represent a common case of basic Web development: getting data from
    151 the database according to a parameter passed in the URL, loading a template and
    152 returning the rendered template. Because this is so common, Django provides a
    153 shortcut, called the "generic views" system.
    154 
    155 Generic views abstract common patterns to the point where you don't even need
    156 to write Python code to write an app.
    157 
    158 Let's convert our poll app to use the generic views system, so we can delete a
    159 bunch of our own code. We'll just have to take a few steps to make the
    160 conversion.
    161 
    162 .. admonition:: Why the code-shuffle?
    163 
    164     Generally, when writing a Django app, you'll evaluate whether generic views
    165     are a good fit for your problem, and you'll use them from the beginning,
    166     rather than refactoring your code halfway through. But this tutorial
    167     intentionally has focused on writing the views "the hard way" until now, to
    168     focus on core concepts.
    169 
    170     You should know basic math before you start using a calculator.
    171 
    172 First, open the polls/urls.py URLconf. It looks like this, according to the
    173 tutorial so far::
    174 
    175     from django.conf.urls.defaults import *
    176 
    177     urlpatterns = patterns('mysite.polls.views',
    178         (r'^$', 'index'),
    179         (r'^(?P<poll_id>\d+)/$', 'detail'),
    180         (r'^(?P<poll_id>\d+)/results/$', 'results'),
    181         (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
    182     )
    183 
    184 Change it like so::
    185 
    186     from django.conf.urls.defaults import *
    187     from mysite.polls.models import Poll
    188 
    189     info_dict = {
    190         'queryset': Poll.objects.all(),
    191     }
    192 
    193     urlpatterns = patterns('',
    194         (r'^$', 'django.views.generic.list_detail.object_list', info_dict),
    195         (r'^(?P<object_id>\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict),
    196         url(r'^(?P<object_id>\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html'), 'poll_results'),
    197         (r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
    198     )
    199 
    200 We're using two generic views here: ``object_list`` and ``object_detail``.
    201 Respectively, those two views abstract the concepts of "display a list of
    202 objects" and "display a detail page for a particular type of object."
    203 
    204     * Each generic view needs to know what data it will be acting upon. This
    205       data is provided in a dictionary. The ``queryset`` key in this dictionary
    206       points to the list of objects to be manipulated by the generic view.
    207 
    208     * The ``object_detail`` generic view expects the ID value captured
    209       from the URL to be called ``"object_id"``, so we've changed ``poll_id`` to
    210       ``object_id`` for the generic views.
    211 
    212     * We've added a name, ``poll_results``, to the results view so that we
    213       have a way to refer to its URL later on (see the documentation about
    214       `naming URL patterns`_ for information). We're also using the `url()`_
    215       function from ``django.conf.urls.defaults`` here. It's a good habit to
    216       use ``url()`` when you are providing a pattern name like this.
    217 
    218 .. _naming URL patterns: ../url_dispatch/#naming-url-patterns
    219 .. _url(): ../url_dispatch/#url
    220 
    221 By default, the ``object_detail`` generic view uses a template called
    222 ``<app name>/<model name>_detail.html``. In our case, it'll use the template
    223 ``"polls/poll_detail.html"``. Thus, rename your ``polls/detail.html`` template to
    224 ``polls/poll_detail.html``, and change the ``render_to_response()`` line in
    225 ``vote()``.
    226 
    227 Similarly, the ``object_list`` generic view uses a template called
    228 ``<app name>/<model name>_list.html``. Thus, rename ``polls/index.html`` to
    229 ``polls/poll_list.html``.
    230 
    231 Because we have more than one entry in the URLconf that uses ``object_detail``
    232 for the polls app, we manually specify a template name for the results view:
    233 ``template_name='polls/results.html'``. Otherwise, both views would use the same
    234 template. Note that we use ``dict()`` to return an altered dictionary in place.
    235 
    236 .. note:: ``all()`` is lazy
    237 
    238     It might look a little frightening to see ``Poll.objects.all()`` being used
    239     in a detail view which only needs one ``Poll`` object, but don't worry;
    240     ``Poll.objects.all()`` is actually a special object called a ``QuerySet``,
    241     which is "lazy" and doesn't hit your database until it absolutely has to. By
    242     the time the database query happens, the ``object_detail`` generic view will
    243     have narrowed its scope down to a single object, so the eventual query will
    244     only select one row from the database.
    245    
    246     If you'd like to know more about how that works, The Django database API
    247     documentation `explains the lazy nature of QuerySet objects`_.
    248 
    249 .. _explains the lazy nature of QuerySet objects: ../db-api/#querysets-are-lazy
    250 
    251 In previous parts of the tutorial, the templates have been provided with a context
    252 that contains the ``poll`` and ``latest_poll_list`` context variables. However,
    253 the generic views provide the variables ``object`` and ``object_list`` as context.
    254 Therefore, you need to change your templates to match the new context variables.
    255 Go through your templates, and modify any reference to ``latest_poll_list`` to
    256 ``object_list``, and change any reference to ``poll`` to ``object``.
    257 
    258 You can now delete the ``index()``, ``detail()`` and ``results()`` views
    259 from ``polls/views.py``. We don't need them anymore -- they have been replaced
    260 by generic views.
    261 
    262 The ``vote()`` view is still required. However, it must be modified to match
    263 the new templates and context variables. Change the template call from
    264 ``polls/detail.html`` to ``polls/poll_detail.html``, and pass ``object`` in the
    265 context instead of ``poll``.
    266 
    267 The last thing to do is fix the URL handling to account for the use of generic
    268 views. In the vote view above, we used the ``reverse()`` function to avoid
    269 hard-coding our URLs. Now that we've switched to a generic view, we'll need to
    270 change the ``reverse()`` call to point back to our new generic view. We can't
    271 simply use the view function anymore -- generic views can be (and are) used
    272 multiple times -- but we can use the name we've given::
    273    
    274     return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))
    275 
    276 Run the server, and use your new polling app based on generic views.
    277 
    278 For full details on generic views, see the `generic views documentation`_.
    279 
    280 .. _generic views documentation: ../generic_views/
    281 
    282 Coming soon
    283 ===========
    284 
    285 The tutorial ends here for the time being. But check back soon for the next
    286 installments:
    287 
    288     * Advanced form processing
    289     * Using the RSS framework
    290     * Using the cache framework
    291     * Using the comments framework
    292     * Advanced admin features: Permissions
    293     * Advanced admin features: Custom JavaScript
    294 
    295 In the meantime, you can read through the rest of the `Django documentation`_
    296 and start writing your own applications.
    297 
    298 .. _Tutorial 3: ../tutorial03/
    299 .. _Django documentation: http://www.djangoproject.com/documentation/
     1=====================================
     2Writing your first Django app, part 4
     3=====================================
     4
     5This tutorial begins where `Tutorial 3`_ left off. We're continuing the Web-poll
     6application and will focus on simple form processing and cutting down our code.
     7
     8Write a simple form
     9===================
     10
     11Let's update our poll detail template ("polls/detail.html") from the last
     12tutorial, so that the template contains an HTML ``<form>`` element::
     13
     14    <h1>{{ poll.question }}</h1>
     15
     16    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
     17
     18    <form action="/polls/{{ poll.id }}/vote/" method="post">
     19    {% for choice in poll.choice_set.all %}
     20        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
     21        <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
     22    {% endfor %}
     23    <input type="submit" value="Vote" />
     24    </form>
     25
     26A quick rundown:
     27
     28    * The above template displays a radio button for each poll choice. The
     29      ``value`` of each radio button is the associated poll choice's ID. The
     30      ``name`` of each radio button is ``"choice"``. That means, when somebody
     31      selects one of the radio buttons and submits the form, it'll send the
     32      POST data ``choice=3``. This is HTML Forms 101.
     33
     34    * We set the form's ``action`` to ``/polls/{{ poll.id }}/vote/``, and we
     35      set ``method="post"``. Using ``method="post"`` (as opposed to
     36      ``method="get"``) is very important, because the act of submitting this
     37      form will alter data server-side. Whenever you create a form that alters
     38      data server-side, use ``method="post"``. This tip isn't specific to
     39      Django; it's just good Web development practice.
     40
     41Now, let's create a Django view that handles the submitted data and does
     42something with it. Remember, in `Tutorial 3`_, we created a URLconf for the
     43polls application that includes this line::
     44
     45    (r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
     46
     47So let's create a ``vote()`` function in ``mysite/polls/views.py``::
     48
     49    from django.shortcuts import get_object_or_404, render_to_response
     50    from django.http import HttpResponseRedirect
     51    from django.core.urlresolvers import reverse
     52    from mysite.polls.models import Choice, Poll
     53    # ...
     54    def vote(request, poll_id):
     55        p = get_object_or_404(Poll, pk=poll_id)
     56        try:
     57            selected_choice = p.choice_set.get(pk=request.POST['choice'])
     58        except (KeyError, Choice.DoesNotExist):
     59            # Redisplay the poll voting form.
     60            return render_to_response('polls/detail.html', {
     61                'poll': p,
     62                'error_message': "You didn't select a choice.",
     63            })
     64        else:
     65            selected_choice.votes += 1
     66            selected_choice.save()
     67            # Always return an HttpResponseRedirect after successfully dealing
     68            # with POST data. This prevents data from being posted twice if a
     69            # user hits the Back button.
     70            return HttpResponseRedirect(reverse('mysite.polls.views.results', args=(p.id,)))
     71
     72This code includes a few things we haven't covered yet in this tutorial:
     73
     74    * ``request.POST`` is a dictionary-like object that lets you access
     75      submitted data by key name. In this case, ``request.POST['choice']``
     76      returns the ID of the selected choice, as a string. ``request.POST``
     77      values are always strings.
     78
     79      Note that Django also provides ``request.GET`` for accessing GET data
     80      in the same way -- but we're explicitly using ``request.POST`` in our
     81      code, to ensure that data is only altered via a POST call.
     82
     83    * ``request.POST['choice']`` will raise ``KeyError`` if ``choice`` wasn't
     84      provided in POST data. The above code checks for ``KeyError`` and
     85      redisplays the poll form with an error message if ``choice`` isn't given.
     86
     87    * After incrementing the choice count, the code returns an
     88      ``HttpResponseRedirect`` rather than a normal ``HttpResponse``.
     89      ``HttpResponseRedirect`` takes a single argument: the URL to which the
     90      user will be redirected (see the following point for how we construct
     91      the URL in this case).
     92
     93      As the Python comment above points out, you should always return an
     94      ``HttpResponseRedirect`` after successfully dealing with POST data. This
     95      tip isn't specific to Django; it's just good Web development practice.
     96
     97    * We are using the ``reverse()`` function in the ``HttpResponseRedirect``
     98      constructor in this example. This function helps avoid having to
     99      hardcode a URL in the view function. It is given the name of the view
     100      that we want to pass control to and the variable portion of the URL
     101      pattern that points to that view. In this case, using the URLConf we set
     102      up in Tutorial 3, this ``reverse()`` call will return a string like ::
     103
     104        '/polls/3/results/'
     105
     106      ... where the ``3`` is the value of ``p.id``. This redirected URL will
     107      then call the ``'results'`` view to display the final page. Note that
     108      you need to use the full name of the view here (including the prefix).
     109
     110      For more information about ``reverse()``, see the `URL dispatcher`_
     111      documentation.
     112
     113As mentioned in Tutorial 3, ``request`` is a ``HTTPRequest`` object. For more
     114on ``HTTPRequest`` objects, see the `request and response documentation`_.
     115
     116After somebody votes in a poll, the ``vote()`` view redirects to the results
     117page for the poll. Let's write that view::
     118
     119    def results(request, poll_id):
     120        p = get_object_or_404(Poll, pk=poll_id)
     121        return render_to_response('polls/results.html', {'poll': p})
     122
     123This is almost exactly the same as the ``detail()`` view from `Tutorial 3`_.
     124The only difference is the template name. We'll fix this redundancy later.
     125
     126Now, create a ``results.html`` template::
     127
     128    <h1>{{ poll.question }}</h1>
     129
     130    <ul>
     131    {% for choice in poll.choice_set.all %}
     132        <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
     133    {% endfor %}
     134    </ul>
     135
     136Now, go to ``/polls/1/`` in your browser and vote in the poll. You should see a
     137results page that gets updated each time you vote. If you submit the form
     138without having chosen a choice, you should see the error message.
     139
     140.. _request and response documentation: ../request_response/
     141.. _URL dispatcher: ../url_dispatch#reverse
     142
     143Because all the views exist, you can now re-enable the admin site.
     144Change ``mysite/urls.py`` so it adds the admin site URL entry to ``urlpatterns``::
     145
     146    from django.conf.urls.defaults import *
     147
     148    urlpatterns = patterns('',
     149       (r'^polls/', include('mysite.polls.urls')),
     150    )
     151
     152    urlpatterns += patterns('',
     153       (r'^admin/', include('django.contrib.admin.urls')),
     154    )
     155
     156Use generic views: Less code is better
     157======================================
     158
     159The ``detail()`` (from `Tutorial 3`_) and ``results()`` views are stupidly
     160simple -- and, as mentioned above, redundant. The ``index()`` view (also from
     161Tutorial 3), which displays a list of polls, is similar.
     162
     163These views represent a common case of basic Web development: getting data from
     164the database according to a parameter passed in the URL, loading a template and
     165returning the rendered template. Because this is so common, Django provides a
     166shortcut, called the "generic views" system.
     167
     168Generic views abstract common patterns to the point where you don't even need
     169to write Python code to write an app.
     170
     171Let's convert our poll app to use the generic views system, so we can delete a
     172bunch of our own code. We'll just have to take a few steps to make the
     173conversion.
     174
     175.. admonition:: Why the code-shuffle?
     176
     177    Generally, when writing a Django app, you'll evaluate whether generic views
     178    are a good fit for your problem, and you'll use them from the beginning,
     179    rather than refactoring your code halfway through. But this tutorial
     180    intentionally has focused on writing the views "the hard way" until now, to
     181    focus on core concepts.
     182
     183    You should know basic math before you start using a calculator.
     184
     185First, open the polls/urls.py URLconf. It looks like this, according to the
     186tutorial so far::
     187
     188    from django.conf.urls.defaults import *
     189
     190    urlpatterns = patterns('mysite.polls.views',
     191        (r'^$', 'index'),
     192        (r'^(?P<poll_id>\d+)/$', 'detail'),
     193        (r'^(?P<poll_id>\d+)/results/$', 'results'),
     194        (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
     195    )
     196
     197Change it like so::
     198
     199    from django.conf.urls.defaults import *
     200    from mysite.polls.models import Poll
     201
     202    info_dict = {
     203        'queryset': Poll.objects.all(),
     204    }
     205
     206    urlpatterns = patterns('',
     207        (r'^$', 'django.views.generic.list_detail.object_list', info_dict),
     208        (r'^(?P<object_id>\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict),
     209        url(r'^(?P<object_id>\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html'), 'poll_results'),
     210        (r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
     211    )
     212
     213We're using two generic views here: ``object_list`` and ``object_detail``.
     214Respectively, those two views abstract the concepts of "display a list of
     215objects" and "display a detail page for a particular type of object."
     216
     217    * Each generic view needs to know what data it will be acting upon. This
     218      data is provided in a dictionary. The ``queryset`` key in this dictionary
     219      points to the list of objects to be manipulated by the generic view.
     220
     221    * The ``object_detail`` generic view expects the ID value captured
     222      from the URL to be called ``"object_id"``, so we've changed ``poll_id`` to
     223      ``object_id`` for the generic views.
     224
     225    * We've added a name, ``poll_results``, to the results view so that we
     226      have a way to refer to its URL later on (see the documentation about
     227      `naming URL patterns`_ for information). We're also using the `url()`_
     228      function from ``django.conf.urls.defaults`` here. It's a good habit to
     229      use ``url()`` when you are providing a pattern name like this.
     230
     231.. _naming URL patterns: ../url_dispatch/#naming-url-patterns
     232.. _url(): ../url_dispatch/#url
     233
     234By default, the ``object_detail`` generic view uses a template called
     235``<app name>/<model name>_detail.html``. In our case, it'll use the template
     236``"polls/poll_detail.html"``. Thus, rename your ``polls/detail.html`` template to
     237``polls/poll_detail.html``, and change the ``render_to_response()`` line in
     238``vote()``.
     239
     240Similarly, the ``object_list`` generic view uses a template called
     241``<app name>/<model name>_list.html``. Thus, rename ``polls/index.html`` to
     242``polls/poll_list.html``.
     243
     244Because we have more than one entry in the URLconf that uses ``object_detail``
     245for the polls app, we manually specify a template name for the results view:
     246``template_name='polls/results.html'``. Otherwise, both views would use the same
     247template. Note that we use ``dict()`` to return an altered dictionary in place.
     248
     249.. note:: ``all()`` is lazy
     250
     251    It might look a little frightening to see ``Poll.objects.all()`` being used
     252    in a detail view which only needs one ``Poll`` object, but don't worry;
     253    ``Poll.objects.all()`` is actually a special object called a ``QuerySet``,
     254    which is "lazy" and doesn't hit your database until it absolutely has to. By
     255    the time the database query happens, the ``object_detail`` generic view will
     256    have narrowed its scope down to a single object, so the eventual query will
     257    only select one row from the database.
     258   
     259    If you'd like to know more about how that works, The Django database API
     260    documentation `explains the lazy nature of QuerySet objects`_.
     261
     262.. _explains the lazy nature of QuerySet objects: ../db-api/#querysets-are-lazy
     263
     264In previous parts of the tutorial, the templates have been provided with a context
     265that contains the ``poll`` and ``latest_poll_list`` context variables. However,
     266the generic views provide the variables ``object`` and ``object_list`` as context.
     267Therefore, you need to change your templates to match the new context variables.
     268Go through your templates, and modify any reference to ``latest_poll_list`` to
     269``object_list``, and change any reference to ``poll`` to ``object``.
     270
     271You can now delete the ``index()``, ``detail()`` and ``results()`` views
     272from ``polls/views.py``. We don't need them anymore -- they have been replaced
     273by generic views.
     274
     275The ``vote()`` view is still required. However, it must be modified to match
     276the new templates and context variables. Change the template call from
     277``polls/detail.html`` to ``polls/poll_detail.html``, and pass ``object`` in the
     278context instead of ``poll``.
     279
     280The last thing to do is fix the URL handling to account for the use of generic
     281views. In the vote view above, we used the ``reverse()`` function to avoid
     282hard-coding our URLs. Now that we've switched to a generic view, we'll need to
     283change the ``reverse()`` call to point back to our new generic view. We can't
     284simply use the view function anymore -- generic views can be (and are) used
     285multiple times -- but we can use the name we've given::
     286   
     287    return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))
     288
     289Run the server, and use your new polling app based on generic views.
     290
     291For full details on generic views, see the `generic views documentation`_.
     292
     293.. _generic views documentation: ../generic_views/
     294
     295Coming soon
     296===========
     297
     298The tutorial ends here for the time being. But check back soon for the next
     299installments:
     300
     301    * Advanced form processing
     302    * Using the RSS framework
     303    * Using the cache framework
     304    * Using the comments framework
     305    * Advanced admin features: Permissions
     306    * Advanced admin features: Custom JavaScript
     307
     308In the meantime, you can read through the rest of the `Django documentation`_
     309and start writing your own applications.
     310
     311.. _Tutorial 3: ../tutorial03/
     312.. _Django documentation: http://www.djangoproject.com/documentation/
Back to Top