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
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">
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 />
27 <input type="submit" value="Vote" />
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
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
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
85 def vote(request, poll_id):
86 p = get_object_or_404(Poll, pk=poll_id)
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', {
93 'error_message': "You didn't select a choice.",
94 }, context_instance=RequestContext(request))
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
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
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
163 Now, create a ``results.html`` template:
165 .. code-block:: html+django
167 <h1>{{ poll.question }}</h1>
170 {% for choice in poll.choice_set.all %}
171 <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
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
200 1. Convert the URLconf.
202 2. Delete some of the old, unneeded views.
204 3. Fix up URL handling for the new views.
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
221 from django.conf.urls import patterns, include, url
223 urlpatterns = patterns('polls.views',
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'),
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('',
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+)/$',
245 template_name='polls/detail.html')),
246 url(r'^(?P<pk>\d+)/results/$',
249 template_name='polls/results.html'),
250 name='poll_results'),
251 url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
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
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>`.
328 The tutorial ends here for the time being. Future installments of the tutorial
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>`