Ticket #18715: 18715.diff

File 18715.diff, 40.9 KB (added by Tim Graham, 12 years ago)

ready for final review?

  • docs/intro/tutorial03.txt

    diff --git a/docs/intro/tutorial03.txt b/docs/intro/tutorial03.txt
    index f350102..3031165 100644
    a b Philosophy  
    1010==========
    1111
    1212A view is a "type" of Web page in your Django application that generally serves
    13 a specific function and has a specific template. For example, in a Weblog
     13a specific function and has a specific template. For example, in a blog
    1414application, you might have the following views:
    1515
    1616* Blog homepage -- displays the latest few entries.
    In our poll application, we'll have the following four views:  
    4141
    4242In Django, each view is represented by a simple Python function.
    4343
    44 Design your URLs
    45 ================
     44Write your first view
     45=====================
     46
     47Let's write the first view. Open the file ``polls/views.py``
     48and put the following Python code in it::
     49
     50    from django.http import HttpResponse
     51
     52    def index(request):
     53        return HttpResponse("Hello, world. You're at the poll index.")
    4654
    47 The first step of writing views is to design your URL structure. You do this by
    48 creating a Python module, called a URLconf. URLconfs are how Django associates
    49 a given URL with given Python code.
     55This is the simplest view possible in Django. Now we have a problem, how does
     56this view get called? For that we need to map it to a URL, in Django this is
     57done in a configuration file called a URLconf.
    5058
    51 When a user requests a Django-powered page, the system looks at the
    52 :setting:`ROOT_URLCONF` setting, which contains a string in Python dotted
    53 syntax. Django loads that module and looks for a module-level variable called
    54 ``urlpatterns``, which is a sequence of tuples in the following format::
     59.. admonition:: What is a URLconf?
    5560
    56     (regular expression, Python callback function [, optional dictionary])
     61    In Django, web pages and other content are delivered by views and
     62    determining which view is called is done by Python modules informally
     63    titled 'URLconfs'. These modules are pure Python code and are a simple
     64    mapping between URL patterns (as simple regular expressions) to Python
     65    callback functions (your views). This tutorial provides basic instruction
     66    in their use, and you can refer to :mod:`django.core.urlresolvers` for
     67    more information.
    5768
    58 Django starts at the first regular expression and makes its way down the list,
    59 comparing the requested URL against each regular expression until it finds one
    60 that matches.
     69To create a URLconf in the polls directory, create a file called ``urls.py``.
     70Your app directory should now look like::
    6171
    62 When it finds a match, Django calls the Python callback function, with an
    63 :class:`~django.http.HttpRequest` object as the first argument, any "captured"
    64 values from the regular expression as keyword arguments, and, optionally,
    65 arbitrary keyword arguments from the dictionary (an optional third item in the
    66 tuple).
     72    polls/
     73        __init__.py
     74        admin.py
     75        models.py
     76        tests.py
     77        urls.py
     78        views.py
    6779
    68 For more on :class:`~django.http.HttpRequest` objects, see the
    69 :doc:`/ref/request-response`. For more details on URLconfs, see the
    70 :doc:`/topics/http/urls`.
     80In the ``polls/urls.py`` file include the following code::
    7181
    72 When you ran ``django-admin.py startproject mysite`` at the beginning of
    73 Tutorial 1, it created a default URLconf in ``mysite/urls.py``. It also
    74 automatically set your :setting:`ROOT_URLCONF` setting (in ``settings.py``) to
    75 point at that file::
     82    from django.conf.urls import patterns, url
    7683
    77     ROOT_URLCONF = 'mysite.urls'
     84    from polls import views
    7885
    79 Time for an example. Edit ``mysite/urls.py`` so it looks like this::
     86    urlpatterns = patterns('',
     87        url(r'^$', views.index, name='index')
     88    )
     89
     90The next step is to point the root URLconf at the ``polls.urls`` module. In
     91``mysite/urls.py`` insert an :func:`~django.conf.urls.include`, leaving you
     92with::
    8093
    8194    from django.conf.urls import patterns, include, url
    8295
    Time for an example. Edit ``mysite/urls.py`` so it looks like this::  
    8497    admin.autodiscover()
    8598
    8699    urlpatterns = patterns('',
    87         url(r'^polls/$', 'polls.views.index'),
    88         url(r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'),
    89         url(r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'),
    90         url(r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
     100        url(r'^polls/', include('polls.urls')),
    91101        url(r'^admin/', include(admin.site.urls)),
    92102    )
    93103
    94 This is worth a review. When somebody requests a page from your Web site -- say,
    95 "/polls/23/", Django will load this Python module, because it's pointed to by
    96 the :setting:`ROOT_URLCONF` setting. It finds the variable named ``urlpatterns``
    97 and traverses the regular expressions in order. When it finds a regular
    98 expression that matches -- ``r'^polls/(?P<poll_id>\d+)/$'`` -- it loads the
    99 function ``detail()`` from ``polls/views.py``. Finally, it calls that
    100 ``detail()`` function like so::
     104You have now wired an `index` view into the URLconf. Go to
     105http://localhost:8000/polls/ in your browser, and you should see the text
     106"*Hello, world. You're at the poll index.*", which you defined in the
     107``index`` view.
    101108
    102     detail(request=<HttpRequest object>, poll_id='23')
     109The :func:`~django.conf.urls.url` function is passed four arguments, two
     110required: ``regex`` and ``view``, and two optional: ``kwargs``, and ``name``.
     111At this point, it's worth reviewing what these arguments are for.
    103112
    104 The ``poll_id='23'`` part comes from ``(?P<poll_id>\d+)``. Using parentheses
    105 around a pattern "captures" the text matched by that pattern and sends it as an
    106 argument to the view function; the ``?P<poll_id>`` defines the name that will be
    107 used to identify the matched pattern; and ``\d+`` is a regular expression to
    108 match a sequence of digits (i.e., a number).
     113:func:`~django.conf.urls.url` argument: regex
     114---------------------------------------------
    109115
    110 Because the URL patterns are regular expressions, there really is no limit on
    111 what you can do with them. And there's no need to add URL cruft such as ``.php``
    112 -- unless you have a sick sense of humor, in which case you can do something
    113 like this::
    114 
    115     (r'^polls/latest\.php$', 'polls.views.index'),
    116 
    117 But, don't do that. It's silly.
     116The term `regex` is a commonly used short form meaning `regular expression`,
     117which is a syntax for matching patterns in strings, or in this case, url
     118patterns. Django starts at the first regular expression and makes its way down
     119the list,  comparing the requested URL against each regular expression until it
     120finds one that matches.
    118121
    119122Note that these regular expressions do not search GET and POST parameters, or
    120 the domain name. For example, in a request to ``http://www.example.com/myapp/``,
    121 the URLconf will look for ``myapp/``. In a request to
    122 ``http://www.example.com/myapp/?page=3``, the URLconf will look for ``myapp/``.
     123the domain name. For example, in a request to
     124``http://www.example.com/myapp/``, the URLconf will look for ``myapp/``. In a
     125request to ``http://www.example.com/myapp/?page=3``, the URLconf will also
     126look for ``myapp/``.
    123127
    124128If you need help with regular expressions, see `Wikipedia's entry`_ and the
    125129documentation of the :mod:`re` module. Also, the O'Reilly book "Mastering
    time the URLconf module is loaded. They're super fast.  
    130134
    131135.. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
    132136
    133 Write your first view
    134 =====================
     137:func:`~django.conf.urls.url` argument: view
     138--------------------------------------------
    135139
    136 Well, we haven't created any views yet -- we just have the URLconf. But let's
    137 make sure Django is following the URLconf properly.
     140When Django finds a regular expression match, Django calls the specified view
     141function, with an :class:`~django.http.HttpRequest` object as the first
     142argument and any “captured” values from the regular expression as other
     143arguments. If the regex uses simple captures, values are passed as positional
     144arguments; if it uses named captures, values are passed as keyword arguments.
     145We'll give an example of this in a bit.
    138146
    139 Fire up the Django development Web server:
     147:func:`~django.conf.urls.url` argument: kwargs
     148----------------------------------------------
    140149
    141 .. code-block:: bash
     150Arbitrary keyword arguments can be passed in a dictionary to the target view. We
     151aren't going to use this feature of Django in the tutorial.
    142152
    143     python manage.py runserver
     153:func:`~django.conf.urls.url` argument: name
     154---------------------------------------------
    144155
    145 Now go to "http://localhost:8000/polls/" on your domain in your Web browser.
    146 You should get a pleasantly-colored error page with the following message::
     156Naming your URL lets you refer to it unambiguously from elsewhere in Django
     157especially templates. This powerful feature allows you to make  global changes
     158to the url patterns of your project while only touching a single file.
    147159
    148     ViewDoesNotExist at /polls/
     160Writing more views
     161==================
    149162
    150     Could not import polls.views.index. View does not exist in module polls.views.
     163Now let's add a few more views to ``polls/views.py``. These views are
     164slightly different, because they take an argument::
    151165
    152 This error happened because you haven't written a function ``index()`` in the
    153 module ``polls/views.py``.
     166    def detail(request, poll_id):
     167        return HttpResponse("You're looking at poll %s." % poll_id)
    154168
    155 Try "/polls/23/", "/polls/23/results/" and "/polls/23/vote/". The error
    156 messages tell you which view Django tried (and failed to find, because you
    157 haven't written any views yet).
     169    def results(request, poll_id):
     170        return HttpResponse("You're looking at the results of poll %s." % poll_id)
    158171
    159 Time to write the first view. Open the file ``polls/views.py``
    160 and put the following Python code in it::
     172    def vote(request, poll_id):
     173        return HttpResponse("You're voting on poll %s." % poll_id)
    161174
    162     from django.http import HttpResponse
     175Wire these news views into the ``polls.urls`` module by adding the following
     176:func:`~django.conf.urls.url` calls::
    163177
    164     def index(request):
    165         return HttpResponse("Hello, world. You're at the poll index.")
     178    from django.conf.urls import patterns, url
    166179
    167 This is the simplest view possible. Go to "/polls/" in your browser, and you
    168 should see your text.
     180    from polls import views
    169181
    170 Now lets add a few more views. These views are slightly different, because
    171 they take an argument (which, remember, is passed in from whatever was
    172 captured by the regular expression in the URLconf)::
     182    urlpatterns = patterns('',
     183        # ex: /polls/
     184        url(r'^$', views.index, name='index'),
     185        # ex: /polls/5/
     186        url(r'^(?P<poll_id>\d+)/$', views.detail, name='detail'),
     187        # ex: /polls/5/results/
     188        url(r'^(?P<poll_id>\d+)/results/$', views.results, name='results'),
     189        # ex: /polls/5/vote/
     190        url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),
     191    )
    173192
    174     def detail(request, poll_id):
    175         return HttpResponse("You're looking at poll %s." % poll_id)
     193Take a look in your browser, at "/polls/34/". It'll run the ``detail()``
     194method and display whatever ID you provide in the URL. Try
     195"/polls/34/results/" and "/polls/34/vote/" too -- these will display the
     196placeholder results and voting pages.
     197
     198When somebody requests a page from your Web site -- say, "/polls/34/", Django
     199will load the ``mysite.urls`` Python module because it's pointed to by the
     200:setting:`ROOT_URLCONF` setting. It finds the variable named ``urlpatterns``
     201and traverses the regular expressions in order. The
     202:func:`~django.conf.urls.include` functions we are using simply reference
     203other URLconfs. Note that the regular expressions for the
     204:func:`~django.conf.urls.include` functions don't have a ``$`` (end-of-string
     205match character) but rather a trailing slash. Whenever Django encounters
     206:func:`~django.conf.urls.include`, it chops off whatever part of the URL
     207matched up to that point and sends the remaining string to the included
     208URLconf for further processing.
    176209
    177     def results(request, poll_id):
    178         return HttpResponse("You're looking at the results of poll %s." % poll_id)
     210The idea behind :func:`~django.conf.urls.include` is to make it easy to
     211plug-and-play URLs. Since polls are in their own URLconf
     212(``polls/urls.py``), they can be placed under "/polls/", or under
     213"/fun_polls/", or under "/content/polls/", or any other path root, and the
     214app will still work.
    179215
    180     def vote(request, poll_id):
    181         return HttpResponse("You're voting on poll %s." % poll_id)
     216Here's what happens if a user goes to "/polls/34/" in this system:
    182217
    183 Take a look in your browser, at "/polls/34/". It'll run the `detail()` method
    184 and display whatever ID you provide in the URL. Try "/polls/34/results/" and
    185 "/polls/34/vote/" too -- these will display the placeholder results and voting
    186 pages.
     218* Django will find the match at ``'^polls/'``
     219
     220* Then, Django will strip off the matching text (``"polls/"``) and send the
     221  remaining text -- ``"34/"`` -- to the 'polls.urls' URLconf for
     222  further processing which matches ``r'^(?P<poll_id>\d+)/$'`` resulting in a
     223  call to the ``detail()`` view like so::
     224
     225    detail(request=<HttpRequest object>, poll_id='34')
     226
     227The ``poll_id='34'`` part comes from ``(?P<poll_id>\d+)``. Using parentheses
     228around a pattern "captures" the text matched by that pattern and sends it as an
     229argument to the view function; ``?P<poll_id>`` defines the name that will
     230be used to identify the matched pattern; and ``\d+`` is a regular expression to
     231match a sequence of digits (i.e., a number).
     232
     233Because the URL patterns are regular expressions, there really is no limit on
     234what you can do with them. And there's no need to add URL cruft such as ``.php``
     235-- unless you have a sick sense of humor, in which case you can do something
     236like this::
     237
     238    (r'^polls/latest\.php$', 'polls.views.index'),
     239
     240But, don't do that. It's silly.
    187241
    188242Write views that actually do something
    189243======================================
    190244
    191 Each view is responsible for doing one of two things: Returning an
     245Each view is responsible for doing one of two things: returning an
    192246:class:`~django.http.HttpResponse` object containing the content for the
    193247requested page, or raising an exception such as :exc:`~django.http.Http404`. The
    194248rest is up to you.
    in :doc:`Tutorial 1 </intro/tutorial01>`. Here's one stab at the ``index()``  
    205259view, which displays the latest 5 poll questions in the system, separated by
    206260commas, according to publication date::
    207261
    208     from polls.models import Poll
    209262    from django.http import HttpResponse
    210263
     264    from polls.models import Poll
     265
    211266    def index(request):
    212         latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
     267        latest_poll_list = Poll.objects.order_by('-pub_date')[:5]
    213268        output = ', '.join([p.question for p in latest_poll_list])
    214269        return HttpResponse(output)
    215270
    216 There's a problem here, though: The page's design is hard-coded in the view. If
     271There's a problem here, though: the page's design is hard-coded in the view. If
    217272you want to change the way the page looks, you'll have to edit this Python code.
    218 So let's use Django's template system to separate the design from Python::
     273So let's use Django's template system to separate the design from Python.
    219274
    220     from django.template import Context, loader
    221     from polls.models import Poll
    222     from django.http import HttpResponse
    223 
    224     def index(request):
    225         latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    226         t = loader.get_template('polls/index.html')
    227         c = Context({
    228             'latest_poll_list': latest_poll_list,
    229         })
    230         return HttpResponse(t.render(c))
    231 
    232 That code loads the template called "polls/index.html" and passes it a context.
    233 The context is a dictionary mapping template variable names to Python objects.
    234 
    235 Reload the page. Now you'll see an error::
    236 
    237     TemplateDoesNotExist at /polls/
    238     polls/index.html
     275First, create a directory, somewhere on your filesystem, whose contents Django
     276can access. (Django runs as whatever user your server runs.) Don't put them
     277under your document root, though. You probably shouldn't make them public, just
     278for security's sake. Then edit :setting:`TEMPLATE_DIRS` in your ``settings.py``
     279to tell Django where it can find templates -- just as you did in the "Customize
     280the admin look and feel" section of Tutorial 2.
    239281
    240 Ah. There's no template yet. First, create a directory, somewhere on your
    241 filesystem, whose contents Django can access. (Django runs as whatever user your
    242 server runs.) Don't put them under your document root, though. You probably
    243 shouldn't make them public, just for security's sake.
    244 Then edit :setting:`TEMPLATE_DIRS` in your ``settings.py`` to tell Django where
    245 it can find templates -- just as you did in the "Customize the admin look and
    246 feel" section of Tutorial 2.
    247 
    248 When you've done that, create a directory ``polls`` in your template directory.
    249 Within that, create a file called ``index.html``. Note that our
    250 ``loader.get_template('polls/index.html')`` code from above maps to
    251 "[template_directory]/polls/index.html" on the filesystem.
    252 
    253 Put the following code in that template:
     282When you've done that, create a directory ``polls`` in your template
     283directory. Within that, create a file called ``index.html``. Put the following
     284code in that template:
    254285
    255286.. code-block:: html+django
    256287
    Put the following code in that template:  
    264295        <p>No polls are available.</p>
    265296    {% endif %}
    266297
     298Now let's use that html template in our index view::
     299
     300    from django.http import HttpResponse
     301    from django.template import Context, loader
     302
     303    from polls.models import Poll
     304
     305    def index(request):
     306        latest_poll_list = Poll.objects.order_by('-pub_date')[:5]
     307        template = loader.get_template('polls/index.html')
     308        context = Context({
     309            'latest_poll_list': latest_poll_list,
     310        })
     311        return HttpResponse(template.render(context))
     312
     313That code loads the template called  ``polls/index.html`` and passes it a
     314context. The context is a dictionary mapping template variable names to Python
     315objects.
     316
    267317Load the page in your Web browser, and you should see a bulleted-list
    268318containing the "What's up" poll from Tutorial 1. The link points to the poll's
    269319detail page.
    270320
    271 A shortcut: render_to_response()
    272 --------------------------------
     321A shortcut: :func:`~django.shortcuts.render`
     322--------------------------------------------
    273323
    274324It's a very common idiom to load a template, fill a context and return an
    275325:class:`~django.http.HttpResponse` object with the result of the rendered
    276326template. Django provides a shortcut. Here's the full ``index()`` view,
    277327rewritten::
    278328
    279     from django.shortcuts import render_to_response
     329    from django.shortcuts import render
     330
    280331    from polls.models import Poll
    281332
    282333    def index(request):
    283334        latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    284         return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
     335        context = {'latest_poll_list': latest_poll_list}
     336        return render(request, 'polls/index.html', context)
    285337
    286338Note that once we've done this in all these views, we no longer need to import
    287339:mod:`~django.template.loader`, :class:`~django.template.Context` and
    288 :class:`~django.http.HttpResponse`.
     340:class:`~django.http.HttpResponse` (you'll want to keep ``HttpReponse`` if you
     341still have the stub methods for ``detail``, ``results``, and ``vote``).
    289342
    290 The :func:`~django.shortcuts.render_to_response` function takes a template name
    291 as its first argument and a dictionary as its optional second argument. It
    292 returns an :class:`~django.http.HttpResponse` object of the given template
    293 rendered with the given context.
     343The :func:`~django.shortcuts.render` function takes the request object as its
     344first argument, a template name as its second argument and a dictionary as its
     345optional third argument. It returns an :class:`~django.http.HttpResponse`
     346object of the given template rendered with the given context.
    294347
    295 Raising 404
    296 ===========
     348Raising a 404 error
     349===================
    297350
    298351Now, let's tackle the poll detail view -- the page that displays the question
    299352for a given poll. Here's the view::
    for a given poll. Here's the view::  
    302355    # ...
    303356    def detail(request, poll_id):
    304357        try:
    305             p = Poll.objects.get(pk=poll_id)
     358            poll = Poll.objects.get(pk=poll_id)
    306359        except Poll.DoesNotExist:
    307360            raise Http404
    308         return render_to_response('polls/detail.html', {'poll': p})
     361        return render(request, 'polls/detail.html', {'poll': poll})
    309362
    310363The new concept here: The view raises the :exc:`~django.http.Http404` exception
    311364if a poll with the requested ID doesn't exist.
    later, but if you'd like to quickly get the above example working, just::  
    317370
    318371will get you started for now.
    319372
    320 A shortcut: get_object_or_404()
    321 -------------------------------
     373A shortcut: :func:`~django.shortcuts.get_object_or_404`
     374-------------------------------------------------------
    322375
    323376It's a very common idiom to use :meth:`~django.db.models.query.QuerySet.get`
    324377and raise :exc:`~django.http.Http404` if the object doesn't exist. Django
    325378provides a shortcut. Here's the ``detail()`` view, rewritten::
    326379
    327     from django.shortcuts import render_to_response, get_object_or_404
     380    from django.shortcuts import render, get_object_or_404
    328381    # ...
    329382    def detail(request, poll_id):
    330         p = get_object_or_404(Poll, pk=poll_id)
    331         return render_to_response('polls/detail.html', {'poll': p})
     383        poll = get_object_or_404(Poll, pk=poll_id)
     384        return render(request, 'polls/detail.html', {'poll': poll})
    332385
    333386The :func:`~django.shortcuts.get_object_or_404` function takes a Django model
    334387as its first argument and an arbitrary number of keyword arguments, which it
    exist.  
    345398    :exc:`~django.core.exceptions.ObjectDoesNotExist`?
    346399
    347400    Because that would couple the model layer to the view layer. One of the
    348     foremost design goals of Django is to maintain loose coupling.
     401    foremost design goals of Django is to maintain loose coupling. Some
     402    controlled coupling is introduced in the :mod:`django.shortcuts` module.
    349403
    350404There's also a :func:`~django.shortcuts.get_list_or_404` function, which works
    351405just as :func:`~django.shortcuts.get_object_or_404` -- except using
    Similarly, your root URLconf may define a ``handler500``, which points  
    387441to a view to call in case of server errors. Server errors happen when
    388442you have runtime errors in view code.
    389443
     444Likewise, you should create a ``500.html`` template at the root of your
     445template directory.
     446
    390447Use the template system
    391448=======================
    392449
    393450Back to the ``detail()`` view for our poll application. Given the context
    394 variable ``poll``, here's what the "polls/detail.html" template might look
     451variable ``poll``, here's what the ``polls/detail.html`` template might look
    395452like:
    396453
    397454.. code-block:: html+django
    suitable for use in the :ttag:`{% for %}<for>` tag.  
    416473
    417474See the :doc:`template guide </topics/templates>` for more about templates.
    418475
    419 Simplifying the URLconfs
    420 ========================
     476Removing hardcoded URLs in templates
     477====================================
    421478
    422 Take some time to play around with the views and template system. As you edit
    423 the URLconf, you may notice there's a fair bit of redundancy in it::
     479Remember, when we wrote the link to a poll in the ``polls/index.html``
     480template, the link was partially hardcoded like this:
    424481
    425     urlpatterns = patterns('',
    426         url(r'^polls/$', 'polls.views.index'),
    427         url(r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'),
    428         url(r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'),
    429         url(r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
    430     )
     482.. code-block:: html+django
    431483
    432 Namely, ``polls.views`` is in every callback.
     484    <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>
    433485
    434 Because this is a common case, the URLconf framework provides a shortcut for
    435 common prefixes. You can factor out the common prefixes and add them as the
    436 first argument to :func:`~django.conf.urls.patterns`, like so::
     486The problem with this hardcoded, tightly-coupled approach is that it becomes
     487challenging to change URLs on projects with a lot of templates. However, since
     488you defined the name argument in the :func:`~django.conf.urls.url` functions in
     489the ``polls.urls`` module, you can remove a reliance on specific URL paths
     490defined in your url configurations by using the ``{% url %}`` template tag:
    437491
    438     urlpatterns = patterns('polls.views',
    439         url(r'^polls/$', 'index'),
    440         url(r'^polls/(?P<poll_id>\d+)/$', 'detail'),
    441         url(r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
    442         url(r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
    443     )
     492.. code-block:: html+django
    444493
    445 This is functionally identical to the previous formatting. It's just a bit
    446 tidier.
     494    <li><a href="{% url 'detail' poll.id %}">{{ poll.question }}</a></li>
    447495
    448 Since you generally don't want the prefix for one app to be applied to every
    449 callback in your URLconf, you can concatenate multiple
    450 :func:`~django.conf.urls.patterns`. Your full ``mysite/urls.py`` might
    451 now look like this::
     496.. note::
    452497
    453     from django.conf.urls import patterns, include, url
     498    If ``{% url 'detail' poll.id %}`` (with quotes) doesn't work, but
     499    ``{% url detail poll.id %}`` (without quotes) does, that means you're
     500    using a version of Django < 1.5. In this case, add the following
     501    declaration at the top of your template:
    454502
    455     from django.contrib import admin
    456     admin.autodiscover()
     503    .. code-block:: html+django
    457504
    458     urlpatterns = patterns('polls.views',
    459         url(r'^polls/$', 'index'),
    460         url(r'^polls/(?P<poll_id>\d+)/$', 'detail'),
    461         url(r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
    462         url(r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
    463     )
     505        {% load url from future %}
    464506
    465     urlpatterns += patterns('',
    466         url(r'^admin/', include(admin.site.urls)),
    467     )
     507The way this works is by looking up the URL definition as specified in the
     508``polls.urls`` module. You can see exactly where the URL name of 'detail' is
     509defined below::
    468510
    469 Decoupling the URLconfs
    470 =======================
     511    ...
     512    # the 'name' value as called by the {% url %} template tag
     513    url(r'^(?P<poll_id>\d+)/$', views.detail, name='detail'),
     514    ...
     515
     516If you want to change the URL of the polls detail view to something else,
     517perhaps to something like ``polls/specifics/12/`` instead of doing it in the
     518template (or templates) you would change it in ``polls/urls.py``::
    471519
    472 While we're at it, we should take the time to decouple our poll-app URLs from
    473 our Django project configuration. Django apps are meant to be pluggable -- that
    474 is, each particular app should be transferable to another Django installation
    475 with minimal fuss.
     520    ...
     521    # added the word 'specifics'
     522    url(r'^specifics/(?P<poll_id>\d+)/$', views.detail, name='detail'),
     523    ...
    476524
    477 Our poll app is pretty decoupled at this point, thanks to the strict directory
    478 structure that ``python manage.py startapp`` created, but one part of it is
    479 coupled to the Django settings: The URLconf.
     525Namespacing URL names
     526======================
    480527
    481 We've been editing the URLs in ``mysite/urls.py``, but the URL design of an
    482 app is specific to the app, not to the Django installation -- so let's move the
    483 URLs within the app directory.
     528The tutorial project has just one app, ``polls``. In real Django projects,
     529there might be five, ten, twenty apps or more. How does Django differentiate
     530the URL names between them? For example, the ``polls`` app has a ``detail``
     531view, and so might an app on the same project that is for a blog. How does one
     532make it so that Django knows which app view to create for a url when using the
     533``{% url %}`` template tag?
    484534
    485 Copy the file ``mysite/urls.py`` to ``polls/urls.py``. Then, change
    486 ``mysite/urls.py`` to remove the poll-specific URLs and insert an
    487 :func:`~django.conf.urls.include`, leaving you with::
     535The answer is to add namespaces to your root URLconf. In the
     536``mysite/urls.py`` file, go ahead and change it to include namespacing::
    488537
    489538    from django.conf.urls import patterns, include, url
    490539
    Copy the file ``mysite/urls.py`` to ``polls/urls.py``. Then, change  
    492541    admin.autodiscover()
    493542
    494543    urlpatterns = patterns('',
    495         url(r'^polls/', include('polls.urls')),
     544        url(r'^polls/', include('polls.urls', namespace="polls")),
    496545        url(r'^admin/', include(admin.site.urls)),
    497546    )
    498547
    499 :func:`~django.conf.urls.include` simply references another URLconf.
    500 Note that the regular expression doesn't have a ``$`` (end-of-string match
    501 character) but has the trailing slash. Whenever Django encounters
    502 :func:`~django.conf.urls.include`, it chops off whatever part of the
    503 URL matched up to that point and sends the remaining string to the included
    504 URLconf for further processing.
    505 
    506 Here's what happens if a user goes to "/polls/34/" in this system:
    507 
    508 * Django will find the match at ``'^polls/'``
    509 
    510 * Then, Django will strip off the matching text (``"polls/"``) and send the
    511   remaining text -- ``"34/"`` -- to the 'polls.urls' URLconf for
    512   further processing.
    513 
    514 Now that we've decoupled that, we need to decouple the ``polls.urls``
    515 URLconf by removing the leading "polls/" from each line, removing the
    516 lines registering the admin site, and removing the ``include`` import which
    517 is no longer used. Your ``polls/urls.py`` file should now look like
    518 this::
    519 
    520     from django.conf.urls import patterns, url
    521 
    522     urlpatterns = patterns('polls.views',
    523         url(r'^$', 'index'),
    524         url(r'^(?P<poll_id>\d+)/$', 'detail'),
    525         url(r'^(?P<poll_id>\d+)/results/$', 'results'),
    526         url(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
    527     )
    528 
    529 The idea behind :func:`~django.conf.urls.include` and URLconf
    530 decoupling is to make it easy to plug-and-play URLs. Now that polls are in their
    531 own URLconf, they can be placed under "/polls/", or under "/fun_polls/", or
    532 under "/content/polls/", or any other path root, and the app will still work.
    533 
    534 All the poll app cares about is its relative path, not its absolute path.
    535 
    536 Removing hardcoded URLs in templates
    537 ------------------------------------
    538 
    539 Remember, when we wrote the link to a poll in our template, the link was
    540 partially hardcoded like this:
     548Now change your ``polls/index.html`` template from:
    541549
    542550.. code-block:: html+django
    543551
    544     <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>
     552    <li><a href="{% url 'detail' poll.id %}">{{ poll.question }}</a></li>
    545553
    546 To use the decoupled URLs we've just introduced, replace the hardcoded link
    547 with the :ttag:`url` template tag:
     554to point at the namespaced detail view:
    548555
    549556.. code-block:: html+django
    550557
    551     <li><a href="{% url 'polls.views.detail' poll.id %}">{{ poll.question }}</a></li>
    552 
    553 .. note::
    554 
    555     If ``{% url 'polls.views.detail' poll.id %}`` (with quotes) doesn't work,
    556     but ``{% url polls.views.detail poll.id %}`` (without quotes) does, that
    557     means you're using a version of Django < 1.5. In this case, add the
    558     following declaration at the top of your template:
    559 
    560     .. code-block:: html+django
    561 
    562         {% load url from future %}
     558    <li><a href="{% url 'polls:detail' poll.id %}">{{ poll.question }}</a></li>
    563559
    564560When you're comfortable with writing views, read :doc:`part 4 of this tutorial
    565561</intro/tutorial04>` to learn about simple form processing and generic views.
  • docs/intro/tutorial04.txt

    diff --git a/docs/intro/tutorial04.txt b/docs/intro/tutorial04.txt
    index 49e597c..8909caf 100644
    a b tutorial, so that the template contains an HTML ``<form>`` element:  
    1818
    1919    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
    2020
    21     <form action="{% url 'polls.views.vote' poll.id %}" method="post">
     21    <form action="{% url 'polls:vote' poll.id %}" method="post">
    2222    {% csrf_token %}
    2323    {% for choice in poll.choice_set.all %}
    2424        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
    A quick rundown:  
    3535  selects one of the radio buttons and submits the form, it'll send the
    3636  POST data ``choice=3``. This is HTML Forms 101.
    3737
    38 * We set the form's ``action`` to ``{% url 'polls.views.vote' poll.id %}``, and we
     38* We set the form's ``action`` to ``{% url 'polls:vote' poll.id %}``, and we
    3939  set ``method="post"``. Using ``method="post"`` (as opposed to
    4040  ``method="get"``) is very important, because the act of submitting this
    4141  form will alter data server-side. Whenever you create a form that alters
    A quick rundown:  
    5252  forms that are targeted at internal URLs should use the
    5353  :ttag:`{% csrf_token %}<csrf_token>` template tag.
    5454
    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 
    7055Now, let's create a Django view that handles the submitted data and does
    7156something with it. Remember, in :doc:`Tutorial 3 </intro/tutorial03>`, we
    7257created a URLconf for the polls application that includes this line::
    7358
    74     (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
     59    url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),
    7560
    7661We also created a dummy implementation of the ``vote()`` function. Let's
    7762create a real version. Add the following to ``polls/views.py``::
    7863
    79     from django.shortcuts import get_object_or_404, render_to_response
     64    from django.shortcuts import get_object_or_404, render
    8065    from django.http import HttpResponseRedirect, HttpResponse
    8166    from django.core.urlresolvers import reverse
    82     from django.template import RequestContext
    8367    from polls.models import Choice, Poll
    8468    # ...
    8569    def vote(request, poll_id):
    create a real version. Add the following to ``polls/views.py``::  
    8872            selected_choice = p.choice_set.get(pk=request.POST['choice'])
    8973        except (KeyError, Choice.DoesNotExist):
    9074            # Redisplay the poll voting form.
    91             return render_to_response('polls/detail.html', {
     75            return render(request, 'polls/detail.html', {
    9276                'poll': p,
    9377                'error_message': "You didn't select a choice.",
    94             }, context_instance=RequestContext(request))
     78            })
    9579        else:
    9680            selected_choice.votes += 1
    9781            selected_choice.save()
    9882            # Always return an HttpResponseRedirect after successfully dealing
    9983            # with POST data. This prevents data from being posted twice if a
    10084            # user hits the Back button.
    101             return HttpResponseRedirect(reverse('polls.views.results', args=(p.id,)))
     85            return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))
    10286
    10387This code includes a few things we haven't covered yet in this tutorial:
    10488
    This code includes a few things we haven't covered yet in this tutorial:  
    142126    '/polls/3/results/'
    143127
    144128  ... 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).
     129  then call the ``'results'`` view to display the final page.
    147130
    148131As mentioned in Tutorial 3, ``request`` is a :class:`~django.http.HttpRequest`
    149132object. For more on :class:`~django.http.HttpRequest` objects, see the
    After somebody votes in a poll, the ``vote()`` view redirects to the results  
    153136page for the poll. Let's write that view::
    154137
    155138    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})
     139        poll = get_object_or_404(Poll, pk=poll_id)
     140        return render(request, 'polls/results.html', {'poll': poll})
    158141
    159142This is almost exactly the same as the ``detail()`` view from :doc:`Tutorial 3
    160143</intro/tutorial03>`. The only difference is the template name. We'll fix this
    161144redundancy later.
    162145
    163 Now, create a ``results.html`` template:
     146Now, create a ``polls/results.html`` template:
    164147
    165148.. code-block:: html+django
    166149
    Now, create a ``results.html`` template:  
    172155    {% endfor %}
    173156    </ul>
    174157
    175     <a href="{% url 'polls.views.detail' poll.id %}">Vote again?</a>
     158    <a href="{% url 'polls:detail' poll.id %}">Vote again?</a>
    176159
    177160Now, go to ``/polls/1/`` in your browser and vote in the poll. You should see a
    178161results page that gets updated each time you vote. If you submit the form
    Read on for details.  
    215198
    216199    You should know basic math before you start using a calculator.
    217200
    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, 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::
     201First, open the ``polls/urls.py`` URLconf and change it like so::
    231202
    232203    from django.conf.urls import patterns, url
    233204    from django.views.generic import DetailView, ListView
    Change it like so::  
    239210                queryset=Poll.objects.order_by('-pub_date')[:5],
    240211                context_object_name='latest_poll_list',
    241212                template_name='polls/index.html'),
    242             name='poll_index'),
     213            name='index'),
    243214        url(r'^(?P<pk>\d+)/$',
    244215            DetailView.as_view(
    245216                model=Poll,
    246217                template_name='polls/detail.html'),
    247             name='poll_detail'),
     218            name='detail'),
    248219        url(r'^(?P<pk>\d+)/results/$',
    249220            DetailView.as_view(
    250221                model=Poll,
    251222                template_name='polls/results.html'),
    252             name='poll_results'),
    253         url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
     223            name='results'),
     224        url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote', name='vote'),
    254225    )
    255226
    256227We're using two generic views here:
    two views abstract the concepts of "display a list of objects" and  
    267238  ``"pk"``, so we've changed ``poll_id`` to ``pk`` for the generic
    268239  views.
    269240
    270 * We've added the ``name`` argument to the views (e.g. ``name='poll_results'``)
    271   so that we have a way to refer to their URL later on (see the
    272   documentation about :ref:`naming URL patterns
    273   <naming-url-patterns>` for information). We're also using the
    274   :func:`~django.conf.urls.url` function from
    275   :mod:`django.conf.urls` here. It's a good habit to use
    276   :func:`~django.conf.urls.url` when you are providing a
    277   pattern name like this.
    278 
    279241By default, the :class:`~django.views.generic.list.DetailView` generic
    280242view uses a template called ``<app name>/<model name>_detail.html``.
    281243In our case, it'll use the template ``"polls/poll_detail.html"``. The
    You can now delete the ``index()``, ``detail()`` and ``results()``  
    308270views from ``polls/views.py``. We don't need them anymore -- they have
    309271been replaced by generic views.
    310272
    311 The last thing to do is fix the URL handling to account for the use of
    312 generic views. In the vote view above, we used the
    313 :func:`~django.core.urlresolvers.reverse` function to avoid
    314 hard-coding our URLs. Now that we've switched to a generic view, we'll
    315 need to change the :func:`~django.core.urlresolvers.reverse` call to
    316 point back to our new generic view. We can't simply use the view
    317 function anymore -- generic views can be (and are) used multiple times
    318 -- but we can use the name we've given::
    319 
    320     return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))
    321 
    322 The same rule apply for the :ttag:`url` template tag. For example in the
    323 ``results.html`` template:
    324 
    325 .. code-block:: html+django
    326 
    327     <a href="{% url 'poll_detail' poll.id %}">Vote again?</a>
    328 
    329273Run the server, and use your new polling app based on generic views.
    330274
    331275For full details on generic views, see the :doc:`generic views documentation
    332276</topics/class-based-views/index>`.
    333277
    334 Coming soon
    335 ===========
    336 
    337 The tutorial ends here for the time being. Future installments of the tutorial
    338 will cover:
    339 
    340 * Advanced form processing
    341 * Using the RSS framework
    342 * Using the cache framework
    343 * Using the comments framework
    344 * Advanced admin features: Permissions
    345 * Advanced admin features: Custom JavaScript
     278What's next?
     279============
    346280
    347 In the meantime, you might want to check out some pointers on :doc:`where to go
    348 from here </intro/whatsnext>`
     281The tutorial ends here for the time being. In the meantime, you might want to
     282check out some pointers on :doc:`where to go from here </intro/whatsnext>`.
Back to Top