~ubuntu-branches/ubuntu/saucy/python-django/saucy-updates

« back to all changes in this revision

Viewing changes to docs/intro/tutorial04.txt

  • Committer: Package Import Robot
  • Author(s): Luke Faraone, Jakub Wilk, Luke Faraone
  • Date: 2013-05-09 15:10:47 UTC
  • mfrom: (1.1.21) (4.4.27 sid)
  • Revision ID: package-import@ubuntu.com-20130509151047-aqv8d71oj9wvcv8c
Tags: 1.5.1-2
[ Jakub Wilk ]
* Use canonical URIs for Vcs-* fields.

[ Luke Faraone ]
* Upload to unstable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 
19
19
    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
20
20
 
21
 
    <form action="/polls/{{ poll.id }}/vote/" method="post">
 
21
    <form action="{% url 'polls:vote' poll.id %}" method="post">
22
22
    {% csrf_token %}
23
23
    {% for choice in poll.choice_set.all %}
24
24
        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
25
 
        <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
 
25
        <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />
26
26
    {% endfor %}
27
27
    <input type="submit" value="Vote" />
28
28
    </form>
33
33
  ``value`` of each radio button is the associated poll choice's ID. The
34
34
  ``name`` of each radio button is ``"choice"``. That means, when somebody
35
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.
 
36
  POST data ``choice=3``. This is the basic concept of HTML forms.
37
37
 
38
 
* We set the form's ``action`` to ``/polls/{{ poll.id }}/vote/``, and we
 
38
* We set the form's ``action`` to ``{% url 'polls:vote' poll.id %}``, and we
39
39
  set ``method="post"``. Using ``method="post"`` (as opposed to
40
40
  ``method="get"``) is very important, because the act of submitting this
41
41
  form will alter data server-side. Whenever you create a form that alters
52
52
  forms that are targeted at internal URLs should use the
53
53
  :ttag:`{% csrf_token %}<csrf_token>` template tag.
54
54
 
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::
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
55
Now, let's create a Django view that handles the submitted data and does
71
56
something with it. Remember, in :doc:`Tutorial 3 </intro/tutorial03>`, we
72
57
created a URLconf for the polls application that includes this line::
73
58
 
74
 
    (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
 
59
    url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),
75
60
 
76
61
We also created a dummy implementation of the ``vote()`` function. Let's
77
62
create a real version. Add the following to ``polls/views.py``::
78
63
 
79
 
    from django.shortcuts import get_object_or_404, render_to_response
 
64
    from django.shortcuts import get_object_or_404, render
80
65
    from django.http import HttpResponseRedirect, HttpResponse
81
66
    from django.core.urlresolvers import reverse
82
 
    from django.template import RequestContext
83
67
    from polls.models import Choice, Poll
84
68
    # ...
85
69
    def vote(request, poll_id):
88
72
            selected_choice = p.choice_set.get(pk=request.POST['choice'])
89
73
        except (KeyError, Choice.DoesNotExist):
90
74
            # Redisplay the poll voting form.
91
 
            return render_to_response('polls/detail.html', {
 
75
            return render(request, 'polls/detail.html', {
92
76
                'poll': p,
93
77
                'error_message': "You didn't select a choice.",
94
 
            }, context_instance=RequestContext(request))
 
78
            })
95
79
        else:
96
80
            selected_choice.votes += 1
97
81
            selected_choice.save()
98
82
            # Always return an HttpResponseRedirect after successfully dealing
99
83
            # with POST data. This prevents data from being posted twice if a
100
84
            # user hits the Back button.
101
 
            return HttpResponseRedirect(reverse('polls.views.results', args=(p.id,)))
 
85
            return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))
102
86
 
103
87
This code includes a few things we haven't covered yet in this tutorial:
104
88
 
114
98
  <django.http.HttpRequest.POST>` in our code, to ensure that data is only
115
99
  altered via a POST call.
116
100
 
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.
 
101
* ``request.POST['choice']`` will raise :exc:`~exceptions.KeyError` if
 
102
  ``choice`` wasn't provided in POST data. The above code checks for
 
103
  :exc:`~exceptions.KeyError` and redisplays the poll form with an error
 
104
  message if ``choice`` isn't given.
120
105
 
121
106
* After incrementing the choice count, the code returns an
122
107
  :class:`~django.http.HttpResponseRedirect` rather than a normal
142
127
    '/polls/3/results/'
143
128
 
144
129
  ... 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).
 
130
  then call the ``'results'`` view to display the final page.
147
131
 
148
132
As mentioned in Tutorial 3, ``request`` is a :class:`~django.http.HttpRequest`
149
133
object. For more on :class:`~django.http.HttpRequest` objects, see the
153
137
page for the poll. Let's write that view::
154
138
 
155
139
    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})
 
140
        poll = get_object_or_404(Poll, pk=poll_id)
 
141
        return render(request, 'polls/results.html', {'poll': poll})
158
142
 
159
143
This is almost exactly the same as the ``detail()`` view from :doc:`Tutorial 3
160
144
</intro/tutorial03>`. The only difference is the template name. We'll fix this
161
145
redundancy later.
162
146
 
163
 
Now, create a ``results.html`` template:
 
147
Now, create a ``polls/results.html`` template:
164
148
 
165
149
.. code-block:: html+django
166
150
 
168
152
 
169
153
    <ul>
170
154
    {% for choice in poll.choice_set.all %}
171
 
        <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
 
155
        <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
172
156
    {% endfor %}
173
157
    </ul>
174
158
 
175
 
    <a href="/polls/{{ poll.id }}/">Vote again?</a>
 
159
    <a href="{% url 'polls:detail' poll.id %}">Vote again?</a>
176
160
 
177
161
Now, go to ``/polls/1/`` in your browser and vote in the poll. You should see a
178
162
results page that gets updated each time you vote. If you submit the form
215
199
 
216
200
    You should know basic math before you start using a calculator.
217
201
 
218
 
First, open the ``polls/urls.py`` URLconf. It looks like this, according to the
219
 
tutorial so far::
220
 
 
221
 
    from django.conf.urls import patterns, include, url
222
 
 
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
 
    )
229
 
 
230
 
Change it like so::
231
 
 
232
 
    from django.conf.urls import patterns, include, url
 
202
Amend URLconf
 
203
-------------
 
204
 
 
205
First, open the ``polls/urls.py`` URLconf and change it like so::
 
206
 
 
207
    from django.conf.urls import patterns, url
233
208
    from django.views.generic import DetailView, ListView
234
209
    from polls.models import Poll
235
210
 
238
213
            ListView.as_view(
239
214
                queryset=Poll.objects.order_by('-pub_date')[:5],
240
215
                context_object_name='latest_poll_list',
241
 
                template_name='polls/index.html')),
 
216
                template_name='polls/index.html'),
 
217
            name='index'),
242
218
        url(r'^(?P<pk>\d+)/$',
243
219
            DetailView.as_view(
244
220
                model=Poll,
245
 
                template_name='polls/detail.html')),
 
221
                template_name='polls/detail.html'),
 
222
            name='detail'),
246
223
        url(r'^(?P<pk>\d+)/results/$',
247
224
            DetailView.as_view(
248
225
                model=Poll,
249
226
                template_name='polls/results.html'),
250
 
            name='poll_results'),
251
 
        url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
 
227
            name='results'),
 
228
        url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote', name='vote'),
252
229
    )
253
230
 
 
231
Amend views
 
232
-----------
 
233
 
254
234
We're using two generic views here:
255
235
:class:`~django.views.generic.list.ListView` and
256
236
:class:`~django.views.generic.detail.DetailView`. Respectively, those
260
240
* Each generic view needs to know what model it will be acting
261
241
  upon. This is provided using the ``model`` parameter.
262
242
 
263
 
* The :class:`~django.views.generic.list.DetailView` generic view
 
243
* The :class:`~django.views.generic.detail.DetailView` generic view
264
244
  expects the primary key value captured from the URL to be called
265
245
  ``"pk"``, so we've changed ``poll_id`` to ``pk`` for the generic
266
246
  views.
267
247
 
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.
276
 
 
277
 
By default, the :class:`~django.views.generic.list.DetailView` generic
 
248
By default, the :class:`~django.views.generic.detail.DetailView` generic
278
249
view uses a template called ``<app name>/<model name>_detail.html``.
279
250
In our case, it'll use the template ``"polls/poll_detail.html"``. The
280
251
``template_name`` argument is used to tell Django to use a specific
282
253
also specify the ``template_name`` for the ``results`` list view --
283
254
this ensures that the results view and the detail view have a
284
255
different appearance when rendered, even though they're both a
285
 
:class:`~django.views.generic.list.DetailView` behind the scenes.
 
256
:class:`~django.views.generic.detail.DetailView` behind the scenes.
286
257
 
287
258
Similarly, the :class:`~django.views.generic.list.ListView` generic
288
259
view uses a default template called ``<app name>/<model
292
263
 
293
264
In previous parts of the tutorial, the templates have been provided
294
265
with a context that contains the ``poll`` and ``latest_poll_list``
295
 
context variables. For DetailView the ``poll`` variable is provided
 
266
context variables. For ``DetailView`` the ``poll`` variable is provided
296
267
automatically -- since we're using a Django model (``Poll``), Django
297
268
is able to determine an appropriate name for the context variable.
298
269
However, for ListView, the automatically generated context variable is
302
273
the new default context variables -- but it's a lot easier to just
303
274
tell Django to use the variable you want.
304
275
 
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.
308
 
 
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
 
 
317
 
    return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))
 
276
You can now delete the ``index()``, ``detail()`` and ``results()`` views from
 
277
``polls/views.py``. We don't need them anymore -- they have been replaced by
 
278
generic views. You can also delete the import for ``HttpResponse``, which is no
 
279
longer required.
318
280
 
319
281
Run the server, and use your new polling app based on generic views.
320
282
 
321
283
For full details on generic views, see the :doc:`generic views documentation
322
 
</topics/http/generic-views>`.
323
 
 
324
 
Coming soon
325
 
===========
326
 
 
327
 
The tutorial ends here for the time being. Future installments of the tutorial
328
 
will cover:
329
 
 
330
 
* Advanced form processing
331
 
* Using the RSS framework
332
 
* Using the cache framework
333
 
* Using the comments framework
334
 
* Advanced admin features: Permissions
335
 
* Advanced admin features: Custom JavaScript
336
 
 
337
 
In the meantime, you might want to check out some pointers on :doc:`where to go
338
 
from here </intro/whatsnext>`
 
284
</topics/class-based-views/index>`.
 
285
 
 
286
When you're comfortable with forms and generic views, read :doc:`part 5 of this
 
287
tutorial</intro/tutorial05>` to learn about testing our polls app.