App Engine Python SDK version 1.9.12
[gae.git] / python / lib / django-0.96 / docs / tutorial03.txt
blob17b6ec0c68882fd20eb538a85ebc31e8a6e5e1e1
1 =====================================
2 Writing your first Django app, part 3
3 =====================================
5 This tutorial begins where `Tutorial 2`_ left off. We're continuing the Web-poll
6 application and will focus on creating the public interface -- "views."
8 .. _Tutorial 2: ../tutorial2/
10 Philosophy
11 ==========
13 A view is a "type" of Web page in your Django application that generally serves
14 a specific function and has a specific template. For example, in a weblog
15 application, you might have the following views:
17     * Blog homepage -- displays the latest few entries.
18     * Entry "detail" page -- permalink page for a single entry.
19     * Year-based archive page -- displays all months with entries in the
20       given year.
21     * Month-based archive page -- displays all days with entries in the
22       given month.
23     * Day-based archive page -- displays all entries in the given day.
24     * Comment action -- handles posting comments to a given entry.
26 In our poll application, we'll have the following four views:
28     * Poll "archive" page -- displays the latest few polls.
29     * Poll "detail" page -- displays a poll question, with no results but
30       with a form to vote.
31     * Poll "results" page -- displays results for a particular poll.
32     * Vote action -- handles voting for a particular choice in a particular
33       poll.
35 In Django, each view is represented by a simple Python function.
37 Design your URLs
38 ================
40 The first step of writing views is to design your URL structure. You do this by
41 creating a Python module, called a URLconf. URLconfs are how Django associates
42 a given URL with given Python code.
44 When a user requests a Django-powered page, the system looks at the
45 ``ROOT_URLCONF`` setting, which contains a string in Python dotted syntax.
46 Django loads that module and looks for a module-level variable called
47 ``urlpatterns``, which is a sequence of tuples in the following format::
49     (regular expression, Python callback function [, optional dictionary])
51 Django starts at the first regular expression and makes its way down the list,
52 comparing the requested URL against each regular expression until it finds one
53 that matches.
55 When it finds a match, Django calls the Python callback function, with an
56 ``HTTPRequest`` object as the first argument, any "captured" values from the
57 regular expression as keyword arguments, and, optionally, arbitrary keyword
58 arguments from the dictionary (an optional third item in the tuple).
60 For more on ``HTTPRequest`` objects, see the `request and response documentation`_.
61 For more details on URLconfs, see the `URLconf documentation`_.
63 When you ran ``python manage.py startproject mysite`` at the beginning of
64 Tutorial 1, it created a default URLconf in ``mysite/urls.py``. It also
65 automatically set your ``ROOT_URLCONF`` setting to point at that file::
67     ROOT_URLCONF = 'mysite.urls'
69 Time for an example. Edit ``mysite/urls.py`` so it looks like this::
71     from django.conf.urls.defaults import *
73     urlpatterns = patterns('',
74         (r'^polls/$', 'mysite.polls.views.index'),
75         (r'^polls/(?P<poll_id>\d+)/$', 'mysite.polls.views.detail'),
76         (r'^polls/(?P<poll_id>\d+)/results/$', 'mysite.polls.views.results'),
77         (r'^polls/(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
78     )
80 This is worth a review. When somebody requests a page from your Web site --
81 say, "/polls/23/", Django will load this Python module, because it's pointed to
82 by the ``ROOT_URLCONF`` setting. It finds the variable named ``urlpatterns``
83 and traverses the regular expressions in order. When it finds a regular
84 expression that matches -- ``r'^polls/(?P<poll_id>\d+)/$'`` -- it loads the
85 associated Python package/module: ``mysite.polls.views.detail``. That
86 corresponds to the function ``detail()`` in ``mysite/polls/views.py``.
87 Finally, it calls that ``detail()`` function like so::
89     detail(request=<HttpRequest object>, poll_id='23')
91 The ``poll_id='23'`` part comes from ``(?P<poll_id>\d+)``. Using parenthesis around a
92 pattern "captures" the text matched by that pattern and sends it as an argument
93 to the view function; the ``?P<poll_id>`` defines the name that will be used to
94 identify the matched pattern; and ``\d+`` is a regular expression to match a sequence of
95 digits (i.e., a number).
97 Because the URL patterns are regular expressions, there really is no limit on
98 what you can do with them. And there's no need to add URL cruft such as
99 ``.php`` -- unless you have a sick sense of humor, in which case you can do
100 something like this::
102     (r'^polls/latest\.php$', 'mysite.polls.views.index'),
104 But, don't do that. It's silly.
106 Note that these regular expressions do not search GET and POST parameters, or
107 the domain name. For example, in a request to ``http://www.example.com/myapp/``,
108 the URLconf will look for ``/myapp/``. In a request to
109 ``http://www.example.com/myapp/?page=3``, the URLconf will look for ``/myapp/``.
111 If you need help with regular expressions, see `Wikipedia's entry`_ and the
112 `Python documentation`_. Also, the O'Reilly book "Mastering Regular
113 Expressions" by Jeffrey Friedl is fantastic.
115 Finally, a performance note: these regular expressions are compiled the first
116 time the URLconf module is loaded. They're super fast.
118 .. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
119 .. _Python documentation: http://www.python.org/doc/current/lib/module-re.html
120 .. _request and response documentation: ../request_response/
121 .. _URLconf documentation: ../url_dispatch/
123 Write your first view
124 =====================
126 Well, we haven't created any views yet -- we just have the URLconf. But let's
127 make sure Django is following the URLconf properly.
129 Fire up the Django development Web server::
131     python manage.py runserver
133 Now go to "http://localhost:8000/polls/" on your domain in your Web browser.
134 You should get a pleasantly-colored error page with the following message::
136     ViewDoesNotExist at /polls/
138     Tried index in module mysite.polls.views. Error was: 'module'
139     object has no attribute 'index'
141 This error happened because you haven't written a function ``index()`` in the
142 module ``mysite/polls/views.py``.
144 Try "/polls/23/", "/polls/23/results/" and "/polls/23/vote/". The error
145 messages tell you which view Django tried (and failed to find, because you
146 haven't written any views yet).
148 Time to write the first view. Open the file ``mysite/polls/views.py``
149 and put the following Python code in it::
151     from django.http import HttpResponse
153     def index(request):
154         return HttpResponse("Hello, world. You're at the poll index.")
156 This is the simplest view possible. Go to "/polls/" in your browser, and you
157 should see your text.
159 Now add the following view. It's slightly different, because it takes an
160 argument (which, remember, is passed in from whatever was captured by the
161 regular expression in the URLconf)::
163     def detail(request, poll_id):
164         return HttpResponse("You're looking at poll %s." % poll_id)
166 Take a look in your browser, at "/polls/34/". It'll display whatever ID you
167 provide in the URL.
169 Write views that actually do something
170 ======================================
172 Each view is responsible for doing one of two things: Returning an ``HttpResponse``
173 object containing the content for the requested page, or raising an exception
174 such as ``Http404``. The rest is up to you.
176 Your view can read records from a database, or not. It can use a template
177 system such as Django's -- or a third-party Python template system -- or not.
178 It can generate a PDF file, output XML, create a ZIP file on the fly, anything
179 you want, using whatever Python libraries you want.
181 All Django wants is that ``HttpResponse``. Or an exception.
183 Because it's convenient, let's use Django's own database API, which we covered
184 in Tutorial 1. Here's one stab at the ``index()`` view, which displays the
185 latest 5 poll questions in the system, separated by commas, according to
186 publication date::
188     from mysite.polls.models import Poll
189     from django.http import HttpResponse
191     def index(request):
192         latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
193         output = ', '.join([p.question for p in latest_poll_list])
194         return HttpResponse(output)
196 There's a problem here, though: The page's design is hard-coded in the view. If
197 you want to change the way the page looks, you'll have to edit this Python code.
198 So let's use Django's template system to separate the design from Python::
200     from django.template import Context, loader
201     from mysite.polls.models import Poll
202     from django.http import HttpResponse
204     def index(request):
205         latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
206         t = loader.get_template('polls/index.html')
207         c = Context({
208             'latest_poll_list': latest_poll_list,
209         })
210         return HttpResponse(t.render(c))
212 That code loads the template called "polls/index.html" and passes it a context. The
213 context is a dictionary mapping template variable names to Python objects.
215 Reload the page. Now you'll see an error::
217     TemplateDoesNotExist at /polls/
218     polls/index.html
220 Ah. There's no template yet. First, create a directory, somewhere on your
221 filesystem, whose contents Django can access. (Django runs as whatever user
222 your server runs.) Don't put them under your document root, though. You
223 probably shouldn't make them public, just for security's sake.
225 Then edit ``TEMPLATE_DIRS`` in your ``settings.py`` to tell Django where it can
226 find templates -- just as you did in the "Customize the admin look and feel"
227 section of Tutorial 2.
229 When you've done that, create a directory ``polls`` in your template directory.
230 Within that, create a file called ``index.html``. Note that our
231 ``loader.get_template('polls/index.html')`` code from above maps to
232 "[template_directory]/polls/index.html" on the filesystem.
234 Put the following code in that template::
236     {% if latest_poll_list %}
237         <ul>
238         {% for poll in latest_poll_list %}
239             <li>{{ poll.question }}</li>
240         {% endfor %}
241         </ul>
242     {% else %}
243         <p>No polls are available.</p>
244     {% endif %}
246 Load the page in your Web browser, and you should see a bulleted-list
247 containing the "What's up" poll from Tutorial 1.
249 A shortcut: render_to_response()
250 --------------------------------
252 It's a very common idiom to load a template, fill a context and return an
253 ``HttpResponse`` object with the result of the rendered template. Django
254 provides a shortcut. Here's the full ``index()`` view, rewritten::
256     from django.shortcuts import render_to_response
257     from mysite.polls.models import Poll
259     def index(request):
260         latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
261         return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
263 Note that once we've done this in all these views, we no longer need to import ``loader``, ``Context`` and ``HttpResponse``.
265 The ``render_to_response()`` function takes a template name as its first
266 argument and a dictionary as its optional second argument. It returns an
267 ``HttpResponse`` object of the given template rendered with the given context.
269 Raising 404
270 ===========
272 Now, let's tackle the poll detail view -- the page that displays the question
273 for a given poll. Here's the view::
275     from django.http import Http404
276     # ...
277     def detail(request, poll_id):
278         try:
279             p = Poll.objects.get(pk=poll_id)
280         except Poll.DoesNotExist:
281             raise Http404
282         return render_to_response('polls/detail.html', {'poll': p})
284 The new concept here: The view raises the ``django.http.Http404``
285 exception if a poll with the requested ID doesn't exist.
287 A shortcut: get_object_or_404()
288 -------------------------------
290 It's a very common idiom to use ``get()`` and raise ``Http404`` if the
291 object doesn't exist. Django provides a shortcut. Here's the ``detail()`` view,
292 rewritten::
294     from django.shortcuts import render_to_response, get_object_or_404
295     # ...
296     def detail(request, poll_id):
297         p = get_object_or_404(Poll, pk=poll_id)
298         return render_to_response('polls/detail.html', {'poll': p})
300 The ``get_object_or_404()`` function takes a Django model module as its first
301 argument and an arbitrary number of keyword arguments, which it passes to the
302 module's ``get()`` function. It raises ``Http404`` if the object doesn't
303 exist.
305 .. admonition:: Philosophy
307     Why do we use a helper function ``get_object_or_404()`` instead of
308     automatically catching the ``DoesNotExist`` exceptions at a higher level,
309     or having the model API raise ``Http404`` instead of ``DoesNotExist``?
311     Because that would couple the model layer to the view layer. One of the
312     foremost design goals of Django is to maintain loose coupling.
314 There's also a ``get_list_or_404()`` function, which works just as
315 ``get_object_or_404()`` -- except using ``filter()`` instead of
316 ``get()``. It raises ``Http404`` if the list is empty.
318 Write a 404 (page not found) view
319 =================================
321 When you raise ``Http404`` from within a view, Django will load a special view
322 devoted to handling 404 errors. It finds it by looking for the variable
323 ``handler404``, which is a string in Python dotted syntax -- the same format
324 the normal URLconf callbacks use. A 404 view itself has nothing special: It's
325 just a normal view.
327 You normally won't have to bother with writing 404 views. By default, URLconfs
328 have the following line up top::
330     from django.conf.urls.defaults import *
332 That takes care of setting ``handler404`` in the current module. As you can see
333 in ``django/conf/urls/defaults.py``, ``handler404`` is set to
334 ``'django.views.defaults.page_not_found'`` by default.
336 Three more things to note about 404 views:
338     * The 404 view is also called if Django doesn't find a match after checking
339       every regular expression in the URLconf.
340     * If you don't define your own 404 view -- and simply use the default,
341       which is recommended -- you still have one obligation: To create a
342       ``404.html`` template in the root of your template directory. The default
343       404 view will use that template for all 404 errors.
344     * If ``DEBUG`` is set to ``True`` (in your settings module) then your 404
345       view will never be used, and the traceback will be displayed instead.
347 Write a 500 (server error) view
348 ===============================
350 Similarly, URLconfs may define a ``handler500``, which points to a view to call
351 in case of server errors. Server errors happen when you have runtime errors in
352 view code.
354 Use the template system
355 =======================
357 Back to our ``polls.detail`` view. Given the context variable ``poll``, here's
358 what the template might look like::
360     <h1>{{ poll.question }}</h1>
361     <ul>
362     {% for choice in poll.choice_set.all %}
363         <li>{{ choice.choice }}</li>
364     {% endfor %}
365     </ul>
367 The template system uses dot-lookup syntax to access variable attributes. In
368 the example of ``{{ poll.question }}``, first Django does a dictionary lookup
369 on the object ``poll``. Failing that, it tries attribute lookup -- which works,
370 in this case. If attribute lookup had failed, it would've tried calling the
371 method ``question()`` on the poll object.
373 Method-calling happens in the ``{% for %}`` loop: ``poll.choice_set.all`` is
374 interpreted as the Python code ``poll.choice_set.all()``, which returns an
375 iterable of Choice objects and is suitable for use in the ``{% for %}`` tag.
377 See the `template guide`_ for full details on how templates work.
379 .. _template guide: ../templates/
381 Simplifying the URLconfs
382 ========================
384 Take some time to play around with the views and template system. As you edit
385 the URLconf, you may notice there's a fair bit of redundancy in it::
387     urlpatterns = patterns('',
388         (r'^polls/$', 'mysite.polls.views.index'),
389         (r'^polls/(?P<poll_id>\d+)/$', 'mysite.polls.views.detail'),
390         (r'^polls/(?P<poll_id>\d+)/results/$', 'mysite.polls.views.results'),
391         (r'^polls/(?P<poll_id>\d+)/vote/$', 'mysite.polls.views.vote'),
392     )
394 Namely, ``mysite.polls.views`` is in every callback.
396 Because this is a common case, the URLconf framework provides a shortcut for
397 common prefixes. You can factor out the common prefixes and add them as the
398 first argument to ``patterns()``, like so::
400     urlpatterns = patterns('mysite.polls.views',
401         (r'^polls/$', 'index'),
402         (r'^polls/(?P<poll_id>\d+)/$', 'detail'),
403         (r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
404         (r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
405     )
407 This is functionally identical to the previous formatting. It's just a bit
408 tidier.
410 Decoupling the URLconfs
411 =======================
413 While we're at it, we should take the time to decouple our poll-app URLs from
414 our Django project configuration. Django apps are meant to be pluggable -- that
415 is, each particular app should be transferrable to another Django installation
416 with minimal fuss.
418 Our poll app is pretty decoupled at this point, thanks to the strict directory
419 structure that ``python manage.py startapp`` created, but one part of it is
420 coupled to the Django settings: The URLconf.
422 We've been editing the URLs in ``mysite/urls.py``, but the URL design of an
423 app is specific to the app, not to the Django installation -- so let's move the
424 URLs within the app directory.
426 Copy the file ``mysite/urls.py`` to ``mysite/polls/urls.py``. Then,
427 change ``mysite/urls.py`` to remove the poll-specific URLs and insert an
428 ``include()``::
430     (r'^polls/', include('mysite.polls.urls')),
432 ``include()``, simply, references another URLconf. Note that the regular
433 expression doesn't have a ``$`` (end-of-string match character) but has the
434 trailing slash. Whenever Django encounters ``include()``, it chops off whatever
435 part of the URL matched up to that point and sends the remaining string to the
436 included URLconf for further processing.
438 Here's what happens if a user goes to "/polls/34/" in this system:
440 * Django will find the match at ``'^polls/'``
441 * It will strip off the matching text (``"polls/"``) and send the remaining
442   text -- ``"34/"`` -- to the 'mysite.polls.urls' urlconf for
443   further processing.
445 Now that we've decoupled that, we need to decouple the
446 'mysite.polls.urls' urlconf by removing the leading "polls/" from each
447 line::
449     urlpatterns = patterns('mysite.polls.views',
450         (r'^$', 'index'),
451         (r'^(?P<poll_id>\d+)/$', 'detail'),
452         (r'^(?P<poll_id>\d+)/results/$', 'results'),
453         (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
454     )
456 The idea behind ``include()`` and URLconf decoupling is to make it easy to
457 plug-and-play URLs. Now that polls are in their own URLconf, they can be placed
458 under "/polls/", or under "/fun_polls/", or under "/content/polls/", or any
459 other URL root, and the app will still work.
461 All the poll app cares about is its relative URLs, not its absolute URLs.
463 When you're comfortable with writing views, read `part 4 of this tutorial`_ to
464 learn about simple form processing and generic views.
466 .. _part 4 of this tutorial: ../tutorial4/