Django

Code

root/django/trunk/docs/tutorial04.txt

Revision 7410, 13.4 kB (checked in by russellm, 1 month ago)

Fixed #6998 -- Corrected typo in Tutorial 4. Thanks, Peter Upfold.

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