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