App Engine Python SDK version 1.7.4 (2)
[gae.git] / python / lib / django_1_4 / docs / intro / tutorial04.txt
blob85d54c34f6ae479dfb710918a87016524dc29991
1 =====================================
2 Writing your first Django app, part 4
3 =====================================
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.
9 Write a simple form
10 ===================
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:
15 .. code-block:: html+django
17     <h1>{{ poll.question }}</h1>
19     {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
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>
30 A quick rundown:
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.
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.
45 * ``forloop.counter`` indicates how many times the :ttag:`for` tag has gone
46   through its loop
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
53   :ttag:`{% csrf_token %}<csrf_token>` template tag.
55 The :ttag:`{% csrf_token %}<csrf_token>` tag requires information from the
56 request object, which is not normally accessible from within the template
57 context. To fix this, a small adjustment needs to be made to the ``detail``
58 view, so that it looks like the following::
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))
67 The details of how this works are explained in the documentation for
68 :ref:`RequestContext <subclassing-context-requestcontext>`.
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::
74     (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
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``::
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,)))
103 This code includes a few things we haven't covered yet in this tutorial:
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.
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.
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.
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).
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.
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   ::
142     '/polls/3/results/'
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).
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>`.
152 After somebody votes in a poll, the ``vote()`` view redirects to the results
153 page for the poll. Let's write that view::
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})
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.
163 Now, create a ``results.html`` template:
165 .. code-block:: html+django
167     <h1>{{ poll.question }}</h1>
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>
175     <a href="/polls/{{ poll.id }}/">Vote again?</a>
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.
181 Use generic views: Less code is better
182 ======================================
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.
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.
193 Generic views abstract common patterns to the point where you don't even need
194 to write Python code to write an app.
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:
200 1. Convert the URLconf.
202 2. Delete some of the old, unneeded views.
204 3. Fix up URL handling for the new views.
206 Read on for details.
208 .. admonition:: Why the code-shuffle?
210     Generally, when writing a Django app, you'll evaluate whether generic views
211     are a good fit for your problem, and you'll use them from the beginning,
212     rather than refactoring your code halfway through. But this tutorial
213     intentionally has focused on writing the views "the hard way" until now, to
214     focus on core concepts.
216     You should know basic math before you start using a calculator.
218 First, open the ``polls/urls.py`` URLconf. It looks like this, according to the
219 tutorial so far::
221     from django.conf.urls import patterns, include, url
223     urlpatterns = patterns('polls.views',
224         url(r'^$', 'index'),
225         url(r'^(?P<poll_id>\d+)/$', 'detail'),
226         url(r'^(?P<poll_id>\d+)/results/$', 'results'),
227         url(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
228     )
230 Change it like so::
232     from django.conf.urls import patterns, include, url
233     from django.views.generic import DetailView, ListView
234     from polls.models import Poll
236     urlpatterns = patterns('',
237         url(r'^$',
238             ListView.as_view(
239                 queryset=Poll.objects.order_by('-pub_date')[:5],
240                 context_object_name='latest_poll_list',
241                 template_name='polls/index.html')),
242         url(r'^(?P<pk>\d+)/$',
243             DetailView.as_view(
244                 model=Poll,
245                 template_name='polls/detail.html')),
246         url(r'^(?P<pk>\d+)/results/$',
247             DetailView.as_view(
248                 model=Poll,
249                 template_name='polls/results.html'),
250             name='poll_results'),
251         url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
252     )
254 We're using two generic views here:
255 :class:`~django.views.generic.list.ListView` and
256 :class:`~django.views.generic.detail.DetailView`. Respectively, those
257 two views abstract the concepts of "display a list of objects" and
258 "display a detail page for a particular type of object."
260 * Each generic view needs to know what model it will be acting
261   upon. This is provided using the ``model`` parameter.
263 * The :class:`~django.views.generic.list.DetailView` generic view
264   expects the primary key value captured from the URL to be called
265   ``"pk"``, so we've changed ``poll_id`` to ``pk`` for the generic
266   views.
268 * We've added a name, ``poll_results``, to the results view so
269   that we have a way to refer to its URL later on (see the
270   documentation about :ref:`naming URL patterns
271   <naming-url-patterns>` for information). We're also using the
272   :func:`~django.conf.urls.url` function from
273   :mod:`django.conf.urls` here. It's a good habit to use
274   :func:`~django.conf.urls.url` when you are providing a
275   pattern name like this.
277 By default, the :class:`~django.views.generic.list.DetailView` generic
278 view uses a template called ``<app name>/<model name>_detail.html``.
279 In our case, it'll use the template ``"polls/poll_detail.html"``. The
280 ``template_name`` argument is used to tell Django to use a specific
281 template name instead of the autogenerated default template name. We
282 also specify the ``template_name`` for the ``results`` list view --
283 this ensures that the results view and the detail view have a
284 different appearance when rendered, even though they're both a
285 :class:`~django.views.generic.list.DetailView` behind the scenes.
287 Similarly, the :class:`~django.views.generic.list.ListView` generic
288 view uses a default template called ``<app name>/<model
289 name>_list.html``; we use ``template_name`` to tell
290 :class:`~django.views.generic.list.ListView` to use our existing
291 ``"polls/index.html"`` template.
293 In previous parts of the tutorial, the templates have been provided
294 with a context that contains the ``poll`` and ``latest_poll_list``
295 context variables. For DetailView the ``poll`` variable is provided
296 automatically -- since we're using a Django model (``Poll``), Django
297 is able to determine an appropriate name for the context variable.
298 However, for ListView, the automatically generated context variable is
299 ``poll_list``. To override this we provide the ``context_object_name``
300 option, specifying that we want to use ``latest_poll_list`` instead.
301 As an alternative approach, you could change your templates to match
302 the new default context variables -- but it's a lot easier to just
303 tell Django to use the variable you want.
305 You can now delete the ``index()``, ``detail()`` and ``results()``
306 views from ``polls/views.py``. We don't need them anymore -- they have
307 been replaced by generic views.
309 The last thing to do is fix the URL handling to account for the use of
310 generic views. In the vote view above, we used the
311 :func:`~django.core.urlresolvers.reverse` function to avoid
312 hard-coding our URLs. Now that we've switched to a generic view, we'll
313 need to change the :func:`~django.core.urlresolvers.reverse` call to
314 point back to our new generic view. We can't simply use the view
315 function anymore -- generic views can be (and are) used multiple times
316 -- but we can use the name we've given::
318     return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))
320 Run the server, and use your new polling app based on generic views.
322 For full details on generic views, see the :doc:`generic views documentation
323 </topics/http/generic-views>`.
325 Coming soon
326 ===========
328 The tutorial ends here for the time being. Future installments of the tutorial
329 will cover:
331 * Advanced form processing
332 * Using the RSS framework
333 * Using the cache framework
334 * Using the comments framework
335 * Advanced admin features: Permissions
336 * Advanced admin features: Custom JavaScript
338 In the meantime, you might want to check out some pointers on :doc:`where to go
339 from here </intro/whatsnext>`