thirdparty/google_appengine/lib/django/docs/tutorial04.txt
changeset 109 620f9b141567
equal deleted inserted replaced
108:261778de26ff 109:620f9b141567
       
     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 from the last tutorial, so that the
       
    12 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 mysite.polls.models import Choice, Poll
       
    52     # ...
       
    53     def vote(request, poll_id):
       
    54         p = get_object_or_404(Poll, pk=poll_id)
       
    55         try:
       
    56             selected_choice = p.choice_set.get(pk=request.POST['choice'])
       
    57         except (KeyError, Choice.DoesNotExist):
       
    58             # Redisplay the poll voting form.
       
    59             return render_to_response('polls/detail.html', {
       
    60                 'poll': p,
       
    61                 'error_message': "You didn't select a choice.",
       
    62             })
       
    63         else:
       
    64             selected_choice.votes += 1
       
    65             selected_choice.save()
       
    66             # Always return an HttpResponseRedirect after successfully dealing
       
    67             # with POST data. This prevents data from being posted twice if a
       
    68             # user hits the Back button.
       
    69             return HttpResponseRedirect('/polls/%s/results/' % p.id)
       
    70 
       
    71 This code includes a few things we haven't covered yet in this tutorial:
       
    72 
       
    73     * ``request.POST`` is a dictionary-like object that lets you access
       
    74       submitted data by key name. In this case, ``request.POST['choice']``
       
    75       returns the ID of the selected choice, as a string. ``request.POST``
       
    76       values are always strings.
       
    77 
       
    78       Note that Django also provides ``request.GET`` for accessing GET data
       
    79       in the same way -- but we're explicitly using ``request.POST`` in our
       
    80       code, to ensure that data is only altered via a POST call.
       
    81 
       
    82     * ``request.POST['choice']`` will raise ``KeyError`` if ``choice`` wasn't
       
    83       provided in POST data. The above code checks for ``KeyError`` and
       
    84       redisplays the poll form with an error message if ``choice`` isn't given.
       
    85 
       
    86     * After incrementing the choice count, the code returns an
       
    87       ``HttpResponseRedirect`` rather than a normal ``HttpResponse``.
       
    88       ``HttpResponseRedirect`` takes a single argument: the URL to which the
       
    89       user will be redirected. You should leave off the "http://" and domain
       
    90       name if you can. That helps your app become portable across domains.
       
    91 
       
    92       As the Python comment above points out, you should always return an
       
    93       ``HttpResponseRedirect`` after successfully dealing with POST data. This
       
    94       tip isn't specific to Django; it's just good Web development practice.
       
    95 
       
    96 As mentioned in Tutorial 3, ``request`` is a ``HTTPRequest`` object. For more
       
    97 on ``HTTPRequest`` objects, see the `request and response documentation`_.
       
    98 
       
    99 After somebody votes in a poll, the ``vote()`` view redirects to the results
       
   100 page for the poll. Let's write that view::
       
   101 
       
   102     def results(request, poll_id):
       
   103         p = get_object_or_404(Poll, pk=poll_id)
       
   104         return render_to_response('polls/results.html', {'poll': p})
       
   105 
       
   106 This is almost exactly the same as the ``detail()`` view from `Tutorial 3`_.
       
   107 The only difference is the template name. We'll fix this redundancy later.
       
   108 
       
   109 Now, create a ``results.html`` template::
       
   110 
       
   111     <h1>{{ poll.question }}</h1>
       
   112 
       
   113     <ul>
       
   114     {% for choice in poll.choice_set.all %}
       
   115         <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
       
   116     {% endfor %}
       
   117     </ul>
       
   118 
       
   119 Now, go to ``/polls/1/`` in your browser and vote in the poll. You should see a
       
   120 results page that gets updated each time you vote. If you submit the form
       
   121 without having chosen a choice, you should see the error message.
       
   122 
       
   123 .. _request and response documentation: ../request_response/
       
   124 
       
   125 Use generic views: Less code is better
       
   126 ======================================
       
   127 
       
   128 The ``detail()`` (from `Tutorial 3`_) and ``results()`` views are stupidly
       
   129 simple -- and, as mentioned above, redundant. The ``index()`` view (also from
       
   130 Tutorial 3), which displays a list of polls, is similar.
       
   131 
       
   132 These views represent a common case of basic Web development: getting data from
       
   133 the database according to a parameter passed in the URL, loading a template and
       
   134 returning the rendered template. Because this is so common, Django provides a
       
   135 shortcut, called the "generic views" system.
       
   136 
       
   137 Generic views abstract common patterns to the point where you don't even need
       
   138 to write Python code to write an app.
       
   139 
       
   140 Let's convert our poll app to use the generic views system, so we can delete a
       
   141 bunch of our own code. We'll just have to take a few steps to make the
       
   142 conversion.
       
   143 
       
   144 .. admonition:: Why the code-shuffle?
       
   145 
       
   146     Generally, when writing a Django app, you'll evaluate whether generic views
       
   147     are a good fit for your problem, and you'll use them from the beginning,
       
   148     rather than refactoring your code halfway through. But this tutorial
       
   149     intentionally has focused on writing the views "the hard way" until now, to
       
   150     focus on core concepts.
       
   151 
       
   152     You should know basic math before you start using a calculator.
       
   153 
       
   154 First, open the polls/urls.py URLconf. It looks like this, according to the
       
   155 tutorial so far::
       
   156 
       
   157     from django.conf.urls.defaults import *
       
   158 
       
   159     urlpatterns = patterns('mysite.polls.views',
       
   160         (r'^$', 'index'),
       
   161         (r'^(?P<poll_id>\d+)/$', 'detail'),
       
   162         (r'^(?P<poll_id>\d+)/results/$', 'results'),
       
   163         (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
       
   164     )
       
   165 
       
   166 Change it like so::
       
   167 
       
   168     from django.conf.urls.defaults import *
       
   169     from mysite.polls.models import Poll
       
   170 
       
   171     info_dict = {
       
   172         'queryset': Poll.objects.all(),
       
   173     }
       
   174 
       
   175     urlpatterns = patterns('',
       
   176         (r'^$', 'django.views.generic.list_detail.object_list', info_dict),
       
   177         (r'^(?P<object_id>\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict),
       
   178         (r'^(?P<object_id>\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html')),
       
   179         (r'^(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
       
   180     )
       
   181 
       
   182 We're using two generic views here: ``object_list`` and ``object_detail``.
       
   183 Respectively, those two views abstract the concepts of "display a list of
       
   184 objects" and "display a detail page for a particular type of object."
       
   185 
       
   186     * Each generic view needs to know what data it will be acting upon. This
       
   187       data is provided in a dictionary. The ``queryset`` key in this dictionary
       
   188       points to the list of objects to be manipulated by the generic view.
       
   189 
       
   190     * The ``object_detail`` generic view expects the ID value captured
       
   191       from the URL to be called ``"object_id"``, so we've changed ``poll_id`` to
       
   192       ``object_id`` for the generic views.
       
   193 
       
   194 By default, the ``object_detail`` generic view uses a template called
       
   195 ``<app name>/<model name>_detail.html``. In our case, it'll use the template
       
   196 ``"polls/poll_detail.html"``. Thus, rename your ``polls/detail.html`` template to
       
   197 ``polls/poll_detail.html``, and change the ``render_to_response()`` line in
       
   198 ``vote()``.
       
   199 
       
   200 Similarly, the ``object_list`` generic view uses a template called
       
   201 ``<app name>/<model name>_list.html``. Thus, rename ``polls/index.html`` to
       
   202 ``polls/poll_list.html``.
       
   203 
       
   204 Because we have more than one entry in the URLconf that uses ``object_detail``
       
   205 for the polls app, we manually specify a template name for the results view:
       
   206 ``template_name='polls/results.html'``. Otherwise, both views would use the same
       
   207 template. Note that we use ``dict()`` to return an altered dictionary in place.
       
   208 
       
   209 .. note:: ``all()`` is lazy
       
   210 
       
   211     It might look a little frightening to see ``Poll.objects.all()`` being used
       
   212     in a detail view which only needs one ``Poll`` object, but don't worry;
       
   213     ``Poll.objects.all()`` is actually a special object called a ``QuerySet``,
       
   214     which is "lazy" and doesn't hit your database until it absolutely has to. By
       
   215     the time the database query happens, the ``object_detail`` generic view will
       
   216     have narrowed its scope down to a single object, so the eventual query will
       
   217     only select one row from the database. 
       
   218     
       
   219     If you'd like to know more about how that works, The Django database API
       
   220     documentation `explains the lazy nature of QuerySet objects`_.
       
   221 
       
   222 .. _explains the lazy nature of QuerySet objects: ../db_api/#querysets-are-lazy
       
   223 
       
   224 In previous parts of the tutorial, the templates have been provided with a context
       
   225 that contains the ``poll`` and ``latest_poll_list`` context variables. However,
       
   226 the generic views provide the variables ``object`` and ``object_list`` as context.
       
   227 Therefore, you need to change your templates to match the new context variables.
       
   228 Go through your templates, and modify any reference to ``latest_poll_list`` to
       
   229 ``object_list``, and change any reference to ``poll`` to ``object``.
       
   230 
       
   231 You can now delete the ``index()``, ``detail()`` and ``results()`` views
       
   232 from ``polls/views.py``. We don't need them anymore -- they have been replaced
       
   233 by generic views.
       
   234 
       
   235 The ``vote()`` view is still required. However, it must be modified to match
       
   236 the new templates and context variables. Change the template call from
       
   237 ``polls/detail.html`` to ``polls/poll_detail.html``, and pass ``object`` in the
       
   238 context instead of ``poll``.
       
   239 
       
   240 Run the server, and use your new polling app based on generic views.
       
   241 
       
   242 For full details on generic views, see the `generic views documentation`_.
       
   243 
       
   244 .. _generic views documentation: ../generic_views/
       
   245 
       
   246 Coming soon
       
   247 ===========
       
   248 
       
   249 The tutorial ends here for the time being. But check back soon for the next
       
   250 installments:
       
   251 
       
   252     * Advanced form processing
       
   253     * Using the RSS framework
       
   254     * Using the cache framework
       
   255     * Using the comments framework
       
   256     * Advanced admin features: Permissions
       
   257     * Advanced admin features: Custom JavaScript
       
   258 
       
   259 .. _Tutorial 3: ../tutorial3/