thirdparty/google_appengine/lib/django/docs/tutorial03.txt
changeset 109 620f9b141567
equal deleted inserted replaced
108:261778de26ff 109:620f9b141567
       
     1 =====================================
       
     2 Writing your first Django app, part 3
       
     3 =====================================
       
     4 
       
     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."
       
     7 
       
     8 .. _Tutorial 2: ../tutorial2/
       
     9 
       
    10 Philosophy
       
    11 ==========
       
    12 
       
    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:
       
    16 
       
    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.
       
    25 
       
    26 In our poll application, we'll have the following four views:
       
    27 
       
    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.
       
    34 
       
    35 In Django, each view is represented by a simple Python function.
       
    36 
       
    37 Design your URLs
       
    38 ================
       
    39 
       
    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.
       
    43 
       
    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::
       
    48 
       
    49     (regular expression, Python callback function [, optional dictionary])
       
    50 
       
    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.
       
    54 
       
    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).
       
    59 
       
    60 For more on ``HTTPRequest`` objects, see the `request and response documentation`_.
       
    61 For more details on URLconfs, see the `URLconf documentation`_.
       
    62 
       
    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::
       
    66 
       
    67     ROOT_URLCONF = 'mysite.urls'
       
    68 
       
    69 Time for an example. Edit ``mysite/urls.py`` so it looks like this::
       
    70 
       
    71     from django.conf.urls.defaults import *
       
    72 
       
    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     )
       
    79 
       
    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::
       
    88 
       
    89     detail(request=<HttpRequest object>, poll_id='23')
       
    90 
       
    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).
       
    96 
       
    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::
       
   101 
       
   102     (r'^polls/latest\.php$', 'mysite.polls.views.index'),
       
   103 
       
   104 But, don't do that. It's silly.
       
   105 
       
   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/``.
       
   110 
       
   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.
       
   114 
       
   115 Finally, a performance note: these regular expressions are compiled the first
       
   116 time the URLconf module is loaded. They're super fast.
       
   117 
       
   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/
       
   122 
       
   123 Write your first view
       
   124 =====================
       
   125 
       
   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.
       
   128 
       
   129 Fire up the Django development Web server::
       
   130 
       
   131     python manage.py runserver
       
   132 
       
   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::
       
   135 
       
   136     ViewDoesNotExist at /polls/
       
   137 
       
   138     Tried index in module mysite.polls.views. Error was: 'module'
       
   139     object has no attribute 'index'
       
   140 
       
   141 This error happened because you haven't written a function ``index()`` in the
       
   142 module ``mysite/polls/views.py``.
       
   143 
       
   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).
       
   147 
       
   148 Time to write the first view. Open the file ``mysite/polls/views.py``
       
   149 and put the following Python code in it::
       
   150 
       
   151     from django.http import HttpResponse
       
   152 
       
   153     def index(request):
       
   154         return HttpResponse("Hello, world. You're at the poll index.")
       
   155 
       
   156 This is the simplest view possible. Go to "/polls/" in your browser, and you
       
   157 should see your text.
       
   158 
       
   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)::
       
   162 
       
   163     def detail(request, poll_id):
       
   164         return HttpResponse("You're looking at poll %s." % poll_id)
       
   165 
       
   166 Take a look in your browser, at "/polls/34/". It'll display whatever ID you
       
   167 provide in the URL.
       
   168 
       
   169 Write views that actually do something
       
   170 ======================================
       
   171 
       
   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.
       
   175 
       
   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.
       
   180 
       
   181 All Django wants is that ``HttpResponse``. Or an exception.
       
   182 
       
   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::
       
   187 
       
   188     from mysite.polls.models import Poll
       
   189     from django.http import HttpResponse
       
   190 
       
   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)
       
   195 
       
   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::
       
   199 
       
   200     from django.template import Context, loader
       
   201     from mysite.polls.models import Poll
       
   202     from django.http import HttpResponse
       
   203 
       
   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))
       
   211 
       
   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.
       
   214 
       
   215 Reload the page. Now you'll see an error::
       
   216 
       
   217     TemplateDoesNotExist at /polls/
       
   218     polls/index.html
       
   219 
       
   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.
       
   224 
       
   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.
       
   228 
       
   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.
       
   233 
       
   234 Put the following code in that template::
       
   235 
       
   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 %}
       
   245 
       
   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.
       
   248 
       
   249 A shortcut: render_to_response()
       
   250 --------------------------------
       
   251 
       
   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::
       
   255 
       
   256     from django.shortcuts import render_to_response
       
   257     from mysite.polls.models import Poll
       
   258 
       
   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})
       
   262 
       
   263 Note that once we've done this in all these views, we no longer need to import ``loader``, ``Context`` and ``HttpResponse``.
       
   264 
       
   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.
       
   268 
       
   269 Raising 404
       
   270 ===========
       
   271 
       
   272 Now, let's tackle the poll detail view -- the page that displays the question
       
   273 for a given poll. Here's the view::
       
   274 
       
   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})
       
   283 
       
   284 The new concept here: The view raises the ``django.http.Http404``
       
   285 exception if a poll with the requested ID doesn't exist.
       
   286 
       
   287 A shortcut: get_object_or_404()
       
   288 -------------------------------
       
   289 
       
   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::
       
   293 
       
   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})
       
   299 
       
   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.
       
   304 
       
   305 .. admonition:: Philosophy
       
   306 
       
   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``?
       
   310 
       
   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.
       
   313 
       
   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.
       
   317 
       
   318 Write a 404 (page not found) view
       
   319 =================================
       
   320 
       
   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.
       
   326 
       
   327 You normally won't have to bother with writing 404 views. By default, URLconfs
       
   328 have the following line up top::
       
   329 
       
   330     from django.conf.urls.defaults import *
       
   331 
       
   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.
       
   335 
       
   336 Three more things to note about 404 views:
       
   337 
       
   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.
       
   346 
       
   347 Write a 500 (server error) view
       
   348 ===============================
       
   349 
       
   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.
       
   353 
       
   354 Use the template system
       
   355 =======================
       
   356 
       
   357 Back to our ``polls.detail`` view. Given the context variable ``poll``, here's
       
   358 what the template might look like::
       
   359 
       
   360     <h1>{{ poll.question }}</h1>
       
   361     <ul>
       
   362     {% for choice in poll.choice_set.all %}
       
   363         <li>{{ choice.choice }}</li>
       
   364     {% endfor %}
       
   365     </ul>
       
   366 
       
   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.
       
   372 
       
   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.
       
   376 
       
   377 See the `template guide`_ for full details on how templates work.
       
   378 
       
   379 .. _template guide: ../templates/
       
   380 
       
   381 Simplifying the URLconfs
       
   382 ========================
       
   383 
       
   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::
       
   386 
       
   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     )
       
   393 
       
   394 Namely, ``mysite.polls.views`` is in every callback.
       
   395 
       
   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::
       
   399 
       
   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     )
       
   406 
       
   407 This is functionally identical to the previous formatting. It's just a bit
       
   408 tidier.
       
   409 
       
   410 Decoupling the URLconfs
       
   411 =======================
       
   412 
       
   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.
       
   417 
       
   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.
       
   421 
       
   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.
       
   425 
       
   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()``::
       
   429 
       
   430     (r'^polls/', include('mysite.polls.urls')),
       
   431 
       
   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.
       
   437 
       
   438 Here's what happens if a user goes to "/polls/34/" in this system:
       
   439 
       
   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.
       
   444 
       
   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::
       
   448 
       
   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     )
       
   455 
       
   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.
       
   460 
       
   461 All the poll app cares about is its relative URLs, not its absolute URLs.
       
   462 
       
   463 When you're comfortable with writing views, read `part 4 of this tutorial`_ to
       
   464 learn about simple form processing and generic views.
       
   465 
       
   466 .. _part 4 of this tutorial: ../tutorial4/