Code

Ticket #18715: 18715.2.diff

File 18715.2.diff, 43.4 KB (added by timo, 21 months ago)

Updated per Russ's feedback

Line 
1diff --git a/docs/intro/tutorial02.txt b/docs/intro/tutorial02.txt
2index fd13230..b87b280 100644
3--- a/docs/intro/tutorial02.txt
4+++ b/docs/intro/tutorial02.txt
5@@ -440,20 +440,30 @@ Open your settings file (``mysite/settings.py``, remember) and look at the
6 filesystem directories to check when loading Django templates. It's a search
7 path.
8 
9+Create a ``mytemplates`` directory in your project directory. Templates can
10+live anywhere on your filesystem that Django can access. (Django runs as
11+whatever user your server runs.) However, keeping your templates within the
12+project is a good convention to follow.
13+
14+When you’ve done that, create a directory polls in your template directory.
15+Within that, create a file called index.html. Note that our
16+``loader.get_template('polls/index.html')`` code from above maps to
17+[template_directory]/polls/index.html” on the filesystem.
18+
19 By default, :setting:`TEMPLATE_DIRS` is empty. So, let's add a line to it, to
20 tell Django where our templates live::
21 
22     TEMPLATE_DIRS = (
23-        '/home/my_username/mytemplates', # Change this to your own directory.
24+        '/path/to/mysite/mytemplates', # Change this to your own directory.
25     )
26 
27 Now copy the template ``admin/base_site.html`` from within the default Django
28 admin template directory in the source code of Django itself
29 (``django/contrib/admin/templates``) into an ``admin`` subdirectory of
30 whichever directory you're using in :setting:`TEMPLATE_DIRS`. For example, if
31-your :setting:`TEMPLATE_DIRS` includes ``'/home/my_username/mytemplates'``, as
32+your :setting:`TEMPLATE_DIRS` includes ``'/path/to/mysite/mytemplates'``, as
33 above, then copy ``django/contrib/admin/templates/admin/base_site.html`` to
34-``/home/my_username/mytemplates/admin/base_site.html``. Don't forget that
35+``/path/to/mysite/mytemplates/admin/base_site.html``. Don't forget that
36 ``admin`` subdirectory.
37 
38 .. admonition:: Where are the Django source files?
39diff --git a/docs/intro/tutorial03.txt b/docs/intro/tutorial03.txt
40index f350102..169e6cd 100644
41--- a/docs/intro/tutorial03.txt
42+++ b/docs/intro/tutorial03.txt
43@@ -10,7 +10,7 @@ Philosophy
44 ==========
45 
46 A view is a "type" of Web page in your Django application that generally serves
47-a specific function and has a specific template. For example, in a Weblog
48+a specific function and has a specific template. For example, in a blog
49 application, you might have the following views:
50 
51 * Blog homepage -- displays the latest few entries.
52@@ -41,42 +41,55 @@ In our poll application, we'll have the following four views:
53 
54 In Django, each view is represented by a simple Python function.
55 
56-Design your URLs
57-================
58+Write your first view
59+=====================
60+
61+Let's write the first view. Open the file ``polls/views.py``
62+and put the following Python code in it::
63+
64+    from django.http import HttpResponse
65+
66+    def index(request):
67+        return HttpResponse("Hello, world. You're at the poll index.")
68 
69-The first step of writing views is to design your URL structure. You do this by
70-creating a Python module, called a URLconf. URLconfs are how Django associates
71-a given URL with given Python code.
72+This is the simplest view possible in Django. Now we have a problem, how does
73+this view get called? For that we need to map it to a URL, in Django this is
74+done in a configuration file called a URLconf.
75 
76-When a user requests a Django-powered page, the system looks at the
77-:setting:`ROOT_URLCONF` setting, which contains a string in Python dotted
78-syntax. Django loads that module and looks for a module-level variable called
79-``urlpatterns``, which is a sequence of tuples in the following format::
80+.. admonition:: What is a URLconf?
81 
82-    (regular expression, Python callback function [, optional dictionary])
83+    In Django, web pages and other content are delivered by views and
84+    determining which view is called is done by Python modules informally
85+    titled 'URLconfs'. These modules are pure Python code and are a simple
86+    mapping between URL patterns (as simple regular expressions) to Python
87+    callback functions (your views). This tutorial provides basic instruction
88+    in their use, and you can refer to :mod:`django.core.urlresolvers` for
89+    more information.
90 
91-Django starts at the first regular expression and makes its way down the list,
92-comparing the requested URL against each regular expression until it finds one
93-that matches.
94+To create a URLconf in the polls directory, create a file called ``urls.py``.
95+Your app directory should now look like::
96 
97-When it finds a match, Django calls the Python callback function, with an
98-:class:`~django.http.HttpRequest` object as the first argument, any "captured"
99-values from the regular expression as keyword arguments, and, optionally,
100-arbitrary keyword arguments from the dictionary (an optional third item in the
101-tuple).
102+    polls/
103+        __init__.py
104+        admin.py
105+        models.py
106+        tests.py
107+        urls.py
108+        views.py
109 
110-For more on :class:`~django.http.HttpRequest` objects, see the
111-:doc:`/ref/request-response`. For more details on URLconfs, see the
112-:doc:`/topics/http/urls`.
113+In the ``polls/urls.py`` file include the following code::
114 
115-When you ran ``django-admin.py startproject mysite`` at the beginning of
116-Tutorial 1, it created a default URLconf in ``mysite/urls.py``. It also
117-automatically set your :setting:`ROOT_URLCONF` setting (in ``settings.py``) to
118-point at that file::
119+    from django.conf.urls import patterns, url
120 
121-    ROOT_URLCONF = 'mysite.urls'
122+    from polls import views
123 
124-Time for an example. Edit ``mysite/urls.py`` so it looks like this::
125+    urlpatterns = patterns('',
126+        url(r'^$', views.index, name='index')
127+    )
128+
129+The next step is to point the root URLconf at the ``polls.urls`` module. In
130+``mysite/urls.py`` insert an :func:`~django.conf.urls.include`, leaving you
131+with::
132 
133     from django.conf.urls import patterns, include, url
134 
135@@ -84,111 +97,156 @@ Time for an example. Edit ``mysite/urls.py`` so it looks like this::
136     admin.autodiscover()
137 
138     urlpatterns = patterns('',
139-        url(r'^polls/$', 'polls.views.index'),
140-        url(r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'),
141-        url(r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'),
142-        url(r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
143+        url(r'^polls/', include('polls.urls')),
144         url(r'^admin/', include(admin.site.urls)),
145     )
146 
147-This is worth a review. When somebody requests a page from your Web site -- say,
148-"/polls/23/", Django will load this Python module, because it's pointed to by
149-the :setting:`ROOT_URLCONF` setting. It finds the variable named ``urlpatterns``
150-and traverses the regular expressions in order. When it finds a regular
151-expression that matches -- ``r'^polls/(?P<poll_id>\d+)/$'`` -- it loads the
152-function ``detail()`` from ``polls/views.py``. Finally, it calls that
153-``detail()`` function like so::
154+You have now wired an `index` view into the URLconf. Go to
155+http://localhost:8000/polls/ in your browser, and you should see the text
156+"*Hello, world. You're at the poll index.*", which you defined in the
157+``index`` view.
158 
159-    detail(request=<HttpRequest object>, poll_id='23')
160+The :func:`~django.conf.urls.url` function is passed four arguments, two
161+required: ``regex`` and ``view``, and two optional: ``kwargs``, and ``name``.
162+At this point, it's worth reviewing what these arguments are for.
163 
164-The ``poll_id='23'`` part comes from ``(?P<poll_id>\d+)``. Using parentheses
165-around a pattern "captures" the text matched by that pattern and sends it as an
166-argument to the view function; the ``?P<poll_id>`` defines the name that will be
167-used to identify the matched pattern; and ``\d+`` is a regular expression to
168-match a sequence of digits (i.e., a number).
169+:func:`~django.conf.urls.url` argument: regex
170+---------------------------------------------
171 
172-Because the URL patterns are regular expressions, there really is no limit on
173-what you can do with them. And there's no need to add URL cruft such as ``.php``
174--- unless you have a sick sense of humor, in which case you can do something
175-like this::
176-
177-    (r'^polls/latest\.php$', 'polls.views.index'),
178-
179-But, don't do that. It's silly.
180+The term `regex` is a commonly used short form meaning `regular expression`,
181+which is a syntax for matching patterns in strings, or in this case, url
182+patterns. Django starts at the first regular expression and makes its way down
183+the list,  comparing the requested URL against each regular expression until it
184+finds one that matches.
185 
186 Note that these regular expressions do not search GET and POST parameters, or
187-the domain name. For example, in a request to ``http://www.example.com/myapp/``,
188-the URLconf will look for ``myapp/``. In a request to
189-``http://www.example.com/myapp/?page=3``, the URLconf will look for ``myapp/``.
190+the domain name. For example, in a request to
191+``http://www.example.com/myapp/``, the URLconf will look for ``myapp/``. In a
192+request to ``http://www.example.com/myapp/?page=3``, the URLconf will also
193+look for ``myapp/``.
194 
195 If you need help with regular expressions, see `Wikipedia's entry`_ and the
196 documentation of the :mod:`re` module. Also, the O'Reilly book "Mastering
197-Regular Expressions" by Jeffrey Friedl is fantastic.
198+Regular Expressions" by Jeffrey Friedl is fantastic. In practice, however,
199+you don't need to be an expert on regular expressions, as you really only need
200+to know how to capture simple patterns. In fact, complex regexes can have poor
201+lookup performance, so you probably shouldn't rely on the full power of regexes.
202 
203 Finally, a performance note: these regular expressions are compiled the first
204-time the URLconf module is loaded. They're super fast.
205+time the URLconf module is loaded. They're super fast (as long as the lookups
206+aren't too complex as noted above).
207 
208 .. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression
209 
210-Write your first view
211-=====================
212+:func:`~django.conf.urls.url` argument: view
213+--------------------------------------------
214 
215-Well, we haven't created any views yet -- we just have the URLconf. But let's
216-make sure Django is following the URLconf properly.
217+When Django finds a regular expression match, Django calls the specified view
218+function, with an :class:`~django.http.HttpRequest` object as the first
219+argument and any “captured” values from the regular expression as other
220+arguments. If the regex uses simple captures, values are passed as positional
221+arguments; if it uses named captures, values are passed as keyword arguments.
222+We'll give an example of this in a bit.
223 
224-Fire up the Django development Web server:
225+:func:`~django.conf.urls.url` argument: kwargs
226+----------------------------------------------
227 
228-.. code-block:: bash
229+Arbitrary keyword arguments can be passed in a dictionary to the target view. We
230+aren't going to use this feature of Django in the tutorial.
231 
232-    python manage.py runserver
233+:func:`~django.conf.urls.url` argument: name
234+---------------------------------------------
235 
236-Now go to "http://localhost:8000/polls/" on your domain in your Web browser.
237-You should get a pleasantly-colored error page with the following message::
238+Naming your URL lets you refer to it unambiguously from elsewhere in Django
239+especially templates. This powerful feature allows you to make  global changes
240+to the url patterns of your project while only touching a single file.
241 
242-    ViewDoesNotExist at /polls/
243+Writing more views
244+==================
245 
246-    Could not import polls.views.index. View does not exist in module polls.views.
247+Now let's add a few more views to ``polls/views.py``. These views are
248+slightly different, because they take an argument::
249 
250-This error happened because you haven't written a function ``index()`` in the
251-module ``polls/views.py``.
252+    def detail(request, poll_id):
253+        return HttpResponse("You're looking at poll %s." % poll_id)
254 
255-Try "/polls/23/", "/polls/23/results/" and "/polls/23/vote/". The error
256-messages tell you which view Django tried (and failed to find, because you
257-haven't written any views yet).
258+    def results(request, poll_id):
259+        return HttpResponse("You're looking at the results of poll %s." % poll_id)
260 
261-Time to write the first view. Open the file ``polls/views.py``
262-and put the following Python code in it::
263+    def vote(request, poll_id):
264+        return HttpResponse("You're voting on poll %s." % poll_id)
265 
266-    from django.http import HttpResponse
267+Wire these news views into the ``polls.urls`` module by adding the following
268+:func:`~django.conf.urls.url` calls::
269 
270-    def index(request):
271-        return HttpResponse("Hello, world. You're at the poll index.")
272+    from django.conf.urls import patterns, url
273 
274-This is the simplest view possible. Go to "/polls/" in your browser, and you
275-should see your text.
276+    from polls import views
277 
278-Now lets add a few more views. These views are slightly different, because
279-they take an argument (which, remember, is passed in from whatever was
280-captured by the regular expression in the URLconf)::
281+    urlpatterns = patterns('',
282+        # ex: /polls/
283+        url(r'^$', views.index, name='index'),
284+        # ex: /polls/5/
285+        url(r'^(?P<poll_id>\d+)/$', views.detail, name='detail'),
286+        # ex: /polls/5/results/
287+        url(r'^(?P<poll_id>\d+)/results/$', views.results, name='results'),
288+        # ex: /polls/5/vote/
289+        url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),
290+    )
291 
292-    def detail(request, poll_id):
293-        return HttpResponse("You're looking at poll %s." % poll_id)
294+Take a look in your browser, at "/polls/34/". It'll run the ``detail()``
295+method and display whatever ID you provide in the URL. Try
296+"/polls/34/results/" and "/polls/34/vote/" too -- these will display the
297+placeholder results and voting pages.
298+
299+When somebody requests a page from your Web site -- say, "/polls/34/", Django
300+will load the ``mysite.urls`` Python module because it's pointed to by the
301+:setting:`ROOT_URLCONF` setting. It finds the variable named ``urlpatterns``
302+and traverses the regular expressions in order. The
303+:func:`~django.conf.urls.include` functions we are using simply reference
304+other URLconfs. Note that the regular expressions for the
305+:func:`~django.conf.urls.include` functions don't have a ``$`` (end-of-string
306+match character) but rather a trailing slash. Whenever Django encounters
307+:func:`~django.conf.urls.include`, it chops off whatever part of the URL
308+matched up to that point and sends the remaining string to the included
309+URLconf for further processing.
310 
311-    def results(request, poll_id):
312-        return HttpResponse("You're looking at the results of poll %s." % poll_id)
313+The idea behind :func:`~django.conf.urls.include` is to make it easy to
314+plug-and-play URLs. Since polls are in their own URLconf
315+(``polls/urls.py``), they can be placed under "/polls/", or under
316+"/fun_polls/", or under "/content/polls/", or any other path root, and the
317+app will still work.
318 
319-    def vote(request, poll_id):
320-        return HttpResponse("You're voting on poll %s." % poll_id)
321+Here's what happens if a user goes to "/polls/34/" in this system:
322 
323-Take a look in your browser, at "/polls/34/". It'll run the `detail()` method
324-and display whatever ID you provide in the URL. Try "/polls/34/results/" and
325-"/polls/34/vote/" too -- these will display the placeholder results and voting
326-pages.
327+* Django will find the match at ``'^polls/'``
328+
329+* Then, Django will strip off the matching text (``"polls/"``) and send the
330+  remaining text -- ``"34/"`` -- to the 'polls.urls' URLconf for
331+  further processing which matches ``r'^(?P<poll_id>\d+)/$'`` resulting in a
332+  call to the ``detail()`` view like so::
333+
334+    detail(request=<HttpRequest object>, poll_id='34')
335+
336+The ``poll_id='34'`` part comes from ``(?P<poll_id>\d+)``. Using parentheses
337+around a pattern "captures" the text matched by that pattern and sends it as an
338+argument to the view function; ``?P<poll_id>`` defines the name that will
339+be used to identify the matched pattern; and ``\d+`` is a regular expression to
340+match a sequence of digits (i.e., a number).
341+
342+Because the URL patterns are regular expressions, there really is no limit on
343+what you can do with them. And there's no need to add URL cruft such as ``.php``
344+-- unless you have a sick sense of humor, in which case you can do something
345+like this::
346+
347+    (r'^polls/latest\.php$', 'polls.views.index'),
348+
349+But, don't do that. It's silly.
350 
351 Write views that actually do something
352 ======================================
353 
354-Each view is responsible for doing one of two things: Returning an
355+Each view is responsible for doing one of two things: returning an
356 :class:`~django.http.HttpResponse` object containing the content for the
357 requested page, or raising an exception such as :exc:`~django.http.Http404`. The
358 rest is up to you.
359@@ -205,51 +263,21 @@ in :doc:`Tutorial 1 </intro/tutorial01>`. Here's one stab at the ``index()``
360 view, which displays the latest 5 poll questions in the system, separated by
361 commas, according to publication date::
362 
363-    from polls.models import Poll
364     from django.http import HttpResponse
365 
366+    from polls.models import Poll
367+
368     def index(request):
369-        latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
370+        latest_poll_list = Poll.objects.order_by('-pub_date')[:5]
371         output = ', '.join([p.question for p in latest_poll_list])
372         return HttpResponse(output)
373 
374-There's a problem here, though: The page's design is hard-coded in the view. If
375+There's a problem here, though: the page's design is hard-coded in the view. If
376 you want to change the way the page looks, you'll have to edit this Python code.
377-So let's use Django's template system to separate the design from Python::
378-
379-    from django.template import Context, loader
380-    from polls.models import Poll
381-    from django.http import HttpResponse
382-
383-    def index(request):
384-        latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
385-        t = loader.get_template('polls/index.html')
386-        c = Context({
387-            'latest_poll_list': latest_poll_list,
388-        })
389-        return HttpResponse(t.render(c))
390-
391-That code loads the template called "polls/index.html" and passes it a context.
392-The context is a dictionary mapping template variable names to Python objects.
393-
394-Reload the page. Now you'll see an error::
395-
396-    TemplateDoesNotExist at /polls/
397-    polls/index.html
398-
399-Ah. There's no template yet. First, create a directory, somewhere on your
400-filesystem, whose contents Django can access. (Django runs as whatever user your
401-server runs.) Don't put them under your document root, though. You probably
402-shouldn't make them public, just for security's sake.
403-Then edit :setting:`TEMPLATE_DIRS` in your ``settings.py`` to tell Django where
404-it can find templates -- just as you did in the "Customize the admin look and
405-feel" section of Tutorial 2.
406-
407-When you've done that, create a directory ``polls`` in your template directory.
408-Within that, create a file called ``index.html``. Note that our
409-``loader.get_template('polls/index.html')`` code from above maps to
410-"[template_directory]/polls/index.html" on the filesystem.
411+So let's use Django's template system to separate the design from Python.
412 
413+First, create a directory ``polls`` in your template directory you specified
414+in setting:`TEMPLATE_DIRS`. Within that, create a file called ``index.html``.
415 Put the following code in that template:
416 
417 .. code-block:: html+django
418@@ -264,36 +292,58 @@ Put the following code in that template:
419         <p>No polls are available.</p>
420     {% endif %}
421 
422+Now let's use that html template in our index view::
423+
424+    from django.http import HttpResponse
425+    from django.template import Context, loader
426+
427+    from polls.models import Poll
428+
429+    def index(request):
430+        latest_poll_list = Poll.objects.order_by('-pub_date')[:5]
431+        template = loader.get_template('polls/index.html')
432+        context = Context({
433+            'latest_poll_list': latest_poll_list,
434+        })
435+        return HttpResponse(template.render(context))
436+
437+That code loads the template called  ``polls/index.html`` and passes it a
438+context. The context is a dictionary mapping template variable names to Python
439+objects.
440+
441 Load the page in your Web browser, and you should see a bulleted-list
442 containing the "What's up" poll from Tutorial 1. The link points to the poll's
443 detail page.
444 
445-A shortcut: render_to_response()
446---------------------------------
447+A shortcut: :func:`~django.shortcuts.render`
448+--------------------------------------------
449 
450 It's a very common idiom to load a template, fill a context and return an
451 :class:`~django.http.HttpResponse` object with the result of the rendered
452 template. Django provides a shortcut. Here's the full ``index()`` view,
453 rewritten::
454 
455-    from django.shortcuts import render_to_response
456+    from django.shortcuts import render
457+
458     from polls.models import Poll
459 
460     def index(request):
461         latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
462-        return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
463+        context = {'latest_poll_list': latest_poll_list}
464+        return render(request, 'polls/index.html', context)
465 
466 Note that once we've done this in all these views, we no longer need to import
467 :mod:`~django.template.loader`, :class:`~django.template.Context` and
468-:class:`~django.http.HttpResponse`.
469+:class:`~django.http.HttpResponse` (you'll want to keep ``HttpResponse`` if you
470+still have the stub methods for ``detail``, ``results``, and ``vote``).
471 
472-The :func:`~django.shortcuts.render_to_response` function takes a template name
473-as its first argument and a dictionary as its optional second argument. It
474-returns an :class:`~django.http.HttpResponse` object of the given template
475-rendered with the given context.
476+The :func:`~django.shortcuts.render` function takes the request object as its
477+first argument, a template name as its second argument and a dictionary as its
478+optional third argument. It returns an :class:`~django.http.HttpResponse`
479+object of the given template rendered with the given context.
480 
481-Raising 404
482-===========
483+Raising a 404 error
484+===================
485 
486 Now, let's tackle the poll detail view -- the page that displays the question
487 for a given poll. Here's the view::
488@@ -302,10 +352,10 @@ for a given poll. Here's the view::
489     # ...
490     def detail(request, poll_id):
491         try:
492-            p = Poll.objects.get(pk=poll_id)
493+            poll = Poll.objects.get(pk=poll_id)
494         except Poll.DoesNotExist:
495             raise Http404
496-        return render_to_response('polls/detail.html', {'poll': p})
497+        return render(request, 'polls/detail.html', {'poll': poll})
498 
499 The new concept here: The view raises the :exc:`~django.http.Http404` exception
500 if a poll with the requested ID doesn't exist.
501@@ -317,18 +367,18 @@ later, but if you'd like to quickly get the above example working, just::
502 
503 will get you started for now.
504 
505-A shortcut: get_object_or_404()
506--------------------------------
507+A shortcut: :func:`~django.shortcuts.get_object_or_404`
508+-------------------------------------------------------
509 
510 It's a very common idiom to use :meth:`~django.db.models.query.QuerySet.get`
511 and raise :exc:`~django.http.Http404` if the object doesn't exist. Django
512 provides a shortcut. Here's the ``detail()`` view, rewritten::
513 
514-    from django.shortcuts import render_to_response, get_object_or_404
515+    from django.shortcuts import render, get_object_or_404
516     # ...
517     def detail(request, poll_id):
518-        p = get_object_or_404(Poll, pk=poll_id)
519-        return render_to_response('polls/detail.html', {'poll': p})
520+        poll = get_object_or_404(Poll, pk=poll_id)
521+        return render(request, 'polls/detail.html', {'poll': poll})
522 
523 The :func:`~django.shortcuts.get_object_or_404` function takes a Django model
524 as its first argument and an arbitrary number of keyword arguments, which it
525@@ -345,7 +395,8 @@ exist.
526     :exc:`~django.core.exceptions.ObjectDoesNotExist`?
527 
528     Because that would couple the model layer to the view layer. One of the
529-    foremost design goals of Django is to maintain loose coupling.
530+    foremost design goals of Django is to maintain loose coupling. Some
531+    controlled coupling is introduced in the :mod:`django.shortcuts` module.
532 
533 There's also a :func:`~django.shortcuts.get_list_or_404` function, which works
534 just as :func:`~django.shortcuts.get_object_or_404` -- except using
535@@ -369,7 +420,8 @@ You normally won't have to bother with writing 404 views. If you don't set
536 is used by default. Optionally, you can create a ``404.html`` template
537 in the root of your template directory. The default 404 view will then use that
538 template for all 404 errors when :setting:`DEBUG` is set to ``False`` (in your
539-settings module).
540+settings module). If you do create the template, add at least some dummy
541+content like "Page not found".
542 
543 A couple more things to note about 404 views:
544 
545@@ -387,11 +439,14 @@ Similarly, your root URLconf may define a ``handler500``, which points
546 to a view to call in case of server errors. Server errors happen when
547 you have runtime errors in view code.
548 
549+Likewise, you should create a ``500.html`` template at the root of your
550+template directory and add some content like "Something went wrong".
551+
552 Use the template system
553 =======================
554 
555 Back to the ``detail()`` view for our poll application. Given the context
556-variable ``poll``, here's what the "polls/detail.html" template might look
557+variable ``poll``, here's what the ``polls/detail.html`` template might look
558 like:
559 
560 .. code-block:: html+django
561@@ -416,75 +471,67 @@ suitable for use in the :ttag:`{% for %}<for>` tag.
562 
563 See the :doc:`template guide </topics/templates>` for more about templates.
564 
565-Simplifying the URLconfs
566-========================
567+Removing hardcoded URLs in templates
568+====================================
569 
570-Take some time to play around with the views and template system. As you edit
571-the URLconf, you may notice there's a fair bit of redundancy in it::
572+Remember, when we wrote the link to a poll in the ``polls/index.html``
573+template, the link was partially hardcoded like this:
574 
575-    urlpatterns = patterns('',
576-        url(r'^polls/$', 'polls.views.index'),
577-        url(r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'),
578-        url(r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'),
579-        url(r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
580-    )
581+.. code-block:: html+django
582 
583-Namely, ``polls.views`` is in every callback.
584+    <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>
585 
586-Because this is a common case, the URLconf framework provides a shortcut for
587-common prefixes. You can factor out the common prefixes and add them as the
588-first argument to :func:`~django.conf.urls.patterns`, like so::
589+The problem with this hardcoded, tightly-coupled approach is that it becomes
590+challenging to change URLs on projects with a lot of templates. However, since
591+you defined the name argument in the :func:`~django.conf.urls.url` functions in
592+the ``polls.urls`` module, you can remove a reliance on specific URL paths
593+defined in your url configurations by using the ``{% url %}`` template tag:
594 
595-    urlpatterns = patterns('polls.views',
596-        url(r'^polls/$', 'index'),
597-        url(r'^polls/(?P<poll_id>\d+)/$', 'detail'),
598-        url(r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
599-        url(r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
600-    )
601+.. code-block:: html+django
602 
603-This is functionally identical to the previous formatting. It's just a bit
604-tidier.
605+    <li><a href="{% url 'detail' poll.id %}">{{ poll.question }}</a></li>
606 
607-Since you generally don't want the prefix for one app to be applied to every
608-callback in your URLconf, you can concatenate multiple
609-:func:`~django.conf.urls.patterns`. Your full ``mysite/urls.py`` might
610-now look like this::
611+.. note::
612 
613-    from django.conf.urls import patterns, include, url
614+    If ``{% url 'detail' poll.id %}`` (with quotes) doesn't work, but
615+    ``{% url detail poll.id %}`` (without quotes) does, that means you're
616+    using a version of Django < 1.5. In this case, add the following
617+    declaration at the top of your template:
618 
619-    from django.contrib import admin
620-    admin.autodiscover()
621+    .. code-block:: html+django
622 
623-    urlpatterns = patterns('polls.views',
624-        url(r'^polls/$', 'index'),
625-        url(r'^polls/(?P<poll_id>\d+)/$', 'detail'),
626-        url(r'^polls/(?P<poll_id>\d+)/results/$', 'results'),
627-        url(r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'),
628-    )
629+        {% load url from future %}
630 
631-    urlpatterns += patterns('',
632-        url(r'^admin/', include(admin.site.urls)),
633-    )
634+The way this works is by looking up the URL definition as specified in the
635+``polls.urls`` module. You can see exactly where the URL name of 'detail' is
636+defined below::
637 
638-Decoupling the URLconfs
639-=======================
640+    ...
641+    # the 'name' value as called by the {% url %} template tag
642+    url(r'^(?P<poll_id>\d+)/$', views.detail, name='detail'),
643+    ...
644 
645-While we're at it, we should take the time to decouple our poll-app URLs from
646-our Django project configuration. Django apps are meant to be pluggable -- that
647-is, each particular app should be transferable to another Django installation
648-with minimal fuss.
649+If you want to change the URL of the polls detail view to something else,
650+perhaps to something like ``polls/specifics/12/`` instead of doing it in the
651+template (or templates) you would change it in ``polls/urls.py``::
652 
653-Our poll app is pretty decoupled at this point, thanks to the strict directory
654-structure that ``python manage.py startapp`` created, but one part of it is
655-coupled to the Django settings: The URLconf.
656+    ...
657+    # added the word 'specifics'
658+    url(r'^specifics/(?P<poll_id>\d+)/$', views.detail, name='detail'),
659+    ...
660 
661-We've been editing the URLs in ``mysite/urls.py``, but the URL design of an
662-app is specific to the app, not to the Django installation -- so let's move the
663-URLs within the app directory.
664+Namespacing URL names
665+======================
666 
667-Copy the file ``mysite/urls.py`` to ``polls/urls.py``. Then, change
668-``mysite/urls.py`` to remove the poll-specific URLs and insert an
669-:func:`~django.conf.urls.include`, leaving you with::
670+The tutorial project has just one app, ``polls``. In real Django projects,
671+there might be five, ten, twenty apps or more. How does Django differentiate
672+the URL names between them? For example, the ``polls`` app has a ``detail``
673+view, and so might an app on the same project that is for a blog. How does one
674+make it so that Django knows which app view to create for a url when using the
675+``{% url %}`` template tag?
676+
677+The answer is to add namespaces to your root URLconf. In the
678+``mysite/urls.py`` file, go ahead and change it to include namespacing::
679 
680     from django.conf.urls import patterns, include, url
681 
682@@ -492,74 +539,21 @@ Copy the file ``mysite/urls.py`` to ``polls/urls.py``. Then, change
683     admin.autodiscover()
684 
685     urlpatterns = patterns('',
686-        url(r'^polls/', include('polls.urls')),
687+        url(r'^polls/', include('polls.urls', namespace="polls")),
688         url(r'^admin/', include(admin.site.urls)),
689     )
690 
691-:func:`~django.conf.urls.include` simply references another URLconf.
692-Note that the regular expression doesn't have a ``$`` (end-of-string match
693-character) but has the trailing slash. Whenever Django encounters
694-:func:`~django.conf.urls.include`, it chops off whatever part of the
695-URL matched up to that point and sends the remaining string to the included
696-URLconf for further processing.
697-
698-Here's what happens if a user goes to "/polls/34/" in this system:
699-
700-* Django will find the match at ``'^polls/'``
701-
702-* Then, Django will strip off the matching text (``"polls/"``) and send the
703-  remaining text -- ``"34/"`` -- to the 'polls.urls' URLconf for
704-  further processing.
705-
706-Now that we've decoupled that, we need to decouple the ``polls.urls``
707-URLconf by removing the leading "polls/" from each line, removing the
708-lines registering the admin site, and removing the ``include`` import which
709-is no longer used. Your ``polls/urls.py`` file should now look like
710-this::
711-
712-    from django.conf.urls import patterns, url
713-
714-    urlpatterns = patterns('polls.views',
715-        url(r'^$', 'index'),
716-        url(r'^(?P<poll_id>\d+)/$', 'detail'),
717-        url(r'^(?P<poll_id>\d+)/results/$', 'results'),
718-        url(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
719-    )
720-
721-The idea behind :func:`~django.conf.urls.include` and URLconf
722-decoupling is to make it easy to plug-and-play URLs. Now that polls are in their
723-own URLconf, they can be placed under "/polls/", or under "/fun_polls/", or
724-under "/content/polls/", or any other path root, and the app will still work.
725-
726-All the poll app cares about is its relative path, not its absolute path.
727-
728-Removing hardcoded URLs in templates
729-------------------------------------
730-
731-Remember, when we wrote the link to a poll in our template, the link was
732-partially hardcoded like this:
733+Now change your ``polls/index.html`` template from:
734 
735 .. code-block:: html+django
736 
737-    <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>
738+    <li><a href="{% url 'detail' poll.id %}">{{ poll.question }}</a></li>
739 
740-To use the decoupled URLs we've just introduced, replace the hardcoded link
741-with the :ttag:`url` template tag:
742+to point at the namespaced detail view:
743 
744 .. code-block:: html+django
745 
746-    <li><a href="{% url 'polls.views.detail' poll.id %}">{{ poll.question }}</a></li>
747-
748-.. note::
749-
750-    If ``{% url 'polls.views.detail' poll.id %}`` (with quotes) doesn't work,
751-    but ``{% url polls.views.detail poll.id %}`` (without quotes) does, that
752-    means you're using a version of Django < 1.5. In this case, add the
753-    following declaration at the top of your template:
754-
755-    .. code-block:: html+django
756-
757-        {% load url from future %}
758+    <li><a href="{% url 'polls:detail' poll.id %}">{{ poll.question }}</a></li>
759 
760 When you're comfortable with writing views, read :doc:`part 4 of this tutorial
761 </intro/tutorial04>` to learn about simple form processing and generic views.
762diff --git a/docs/intro/tutorial04.txt b/docs/intro/tutorial04.txt
763index 49e597c..8909caf 100644
764--- a/docs/intro/tutorial04.txt
765+++ b/docs/intro/tutorial04.txt
766@@ -18,7 +18,7 @@ tutorial, so that the template contains an HTML ``<form>`` element:
767 
768     {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
769 
770-    <form action="{% url 'polls.views.vote' poll.id %}" method="post">
771+    <form action="{% url 'polls:vote' poll.id %}" method="post">
772     {% csrf_token %}
773     {% for choice in poll.choice_set.all %}
774         <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
775@@ -35,7 +35,7 @@ A quick rundown:
776   selects one of the radio buttons and submits the form, it'll send the
777   POST data ``choice=3``. This is HTML Forms 101.
778 
779-* We set the form's ``action`` to ``{% url 'polls.views.vote' poll.id %}``, and we
780+* We set the form's ``action`` to ``{% url 'polls:vote' poll.id %}``, and we
781   set ``method="post"``. Using ``method="post"`` (as opposed to
782   ``method="get"``) is very important, because the act of submitting this
783   form will alter data server-side. Whenever you create a form that alters
784@@ -52,34 +52,18 @@ A quick rundown:
785   forms that are targeted at internal URLs should use the
786   :ttag:`{% csrf_token %}<csrf_token>` template tag.
787 
788-The :ttag:`{% csrf_token %}<csrf_token>` tag requires information from the
789-request object, which is not normally accessible from within the template
790-context. To fix this, a small adjustment needs to be made to the ``detail``
791-view, so that it looks like the following::
792-
793-    from django.template import RequestContext
794-    # ...
795-    def detail(request, poll_id):
796-        p = get_object_or_404(Poll, pk=poll_id)
797-        return render_to_response('polls/detail.html', {'poll': p},
798-                                   context_instance=RequestContext(request))
799-
800-The details of how this works are explained in the documentation for
801-:ref:`RequestContext <subclassing-context-requestcontext>`.
802-
803 Now, let's create a Django view that handles the submitted data and does
804 something with it. Remember, in :doc:`Tutorial 3 </intro/tutorial03>`, we
805 created a URLconf for the polls application that includes this line::
806 
807-    (r'^(?P<poll_id>\d+)/vote/$', 'vote'),
808+    url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),
809 
810 We also created a dummy implementation of the ``vote()`` function. Let's
811 create a real version. Add the following to ``polls/views.py``::
812 
813-    from django.shortcuts import get_object_or_404, render_to_response
814+    from django.shortcuts import get_object_or_404, render
815     from django.http import HttpResponseRedirect, HttpResponse
816     from django.core.urlresolvers import reverse
817-    from django.template import RequestContext
818     from polls.models import Choice, Poll
819     # ...
820     def vote(request, poll_id):
821@@ -88,17 +72,17 @@ create a real version. Add the following to ``polls/views.py``::
822             selected_choice = p.choice_set.get(pk=request.POST['choice'])
823         except (KeyError, Choice.DoesNotExist):
824             # Redisplay the poll voting form.
825-            return render_to_response('polls/detail.html', {
826+            return render(request, 'polls/detail.html', {
827                 'poll': p,
828                 'error_message': "You didn't select a choice.",
829-            }, context_instance=RequestContext(request))
830+            })
831         else:
832             selected_choice.votes += 1
833             selected_choice.save()
834             # Always return an HttpResponseRedirect after successfully dealing
835             # with POST data. This prevents data from being posted twice if a
836             # user hits the Back button.
837-            return HttpResponseRedirect(reverse('polls.views.results', args=(p.id,)))
838+            return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))
839 
840 This code includes a few things we haven't covered yet in this tutorial:
841 
842@@ -142,8 +126,7 @@ This code includes a few things we haven't covered yet in this tutorial:
843     '/polls/3/results/'
844 
845   ... where the ``3`` is the value of ``p.id``. This redirected URL will
846-  then call the ``'results'`` view to display the final page. Note that you
847-  need to use the full name of the view here (including the prefix).
848+  then call the ``'results'`` view to display the final page.
849 
850 As mentioned in Tutorial 3, ``request`` is a :class:`~django.http.HttpRequest`
851 object. For more on :class:`~django.http.HttpRequest` objects, see the
852@@ -153,14 +136,14 @@ After somebody votes in a poll, the ``vote()`` view redirects to the results
853 page for the poll. Let's write that view::
854 
855     def results(request, poll_id):
856-        p = get_object_or_404(Poll, pk=poll_id)
857-        return render_to_response('polls/results.html', {'poll': p})
858+        poll = get_object_or_404(Poll, pk=poll_id)
859+        return render(request, 'polls/results.html', {'poll': poll})
860 
861 This is almost exactly the same as the ``detail()`` view from :doc:`Tutorial 3
862 </intro/tutorial03>`. The only difference is the template name. We'll fix this
863 redundancy later.
864 
865-Now, create a ``results.html`` template:
866+Now, create a ``polls/results.html`` template:
867 
868 .. code-block:: html+django
869 
870@@ -172,7 +155,7 @@ Now, create a ``results.html`` template:
871     {% endfor %}
872     </ul>
873 
874-    <a href="{% url 'polls.views.detail' poll.id %}">Vote again?</a>
875+    <a href="{% url 'polls:detail' poll.id %}">Vote again?</a>
876 
877 Now, go to ``/polls/1/`` in your browser and vote in the poll. You should see a
878 results page that gets updated each time you vote. If you submit the form
879@@ -215,19 +198,7 @@ Read on for details.
880 
881     You should know basic math before you start using a calculator.
882 
883-First, open the ``polls/urls.py`` URLconf. It looks like this, according to the
884-tutorial so far::
885-
886-    from django.conf.urls import patterns, url
887-
888-    urlpatterns = patterns('polls.views',
889-        url(r'^$', 'index'),
890-        url(r'^(?P<poll_id>\d+)/$', 'detail'),
891-        url(r'^(?P<poll_id>\d+)/results/$', 'results'),
892-        url(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
893-    )
894-
895-Change it like so::
896+First, open the ``polls/urls.py`` URLconf and change it like so::
897 
898     from django.conf.urls import patterns, url
899     from django.views.generic import DetailView, ListView
900@@ -239,18 +210,18 @@ Change it like so::
901                 queryset=Poll.objects.order_by('-pub_date')[:5],
902                 context_object_name='latest_poll_list',
903                 template_name='polls/index.html'),
904-            name='poll_index'),
905+            name='index'),
906         url(r'^(?P<pk>\d+)/$',
907             DetailView.as_view(
908                 model=Poll,
909                 template_name='polls/detail.html'),
910-            name='poll_detail'),
911+            name='detail'),
912         url(r'^(?P<pk>\d+)/results/$',
913             DetailView.as_view(
914                 model=Poll,
915                 template_name='polls/results.html'),
916-            name='poll_results'),
917-        url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
918+            name='results'),
919+        url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote', name='vote'),
920     )
921 
922 We're using two generic views here:
923@@ -267,15 +238,6 @@ two views abstract the concepts of "display a list of objects" and
924   ``"pk"``, so we've changed ``poll_id`` to ``pk`` for the generic
925   views.
926 
927-* We've added the ``name`` argument to the views (e.g. ``name='poll_results'``)
928-  so that we have a way to refer to their URL later on (see the
929-  documentation about :ref:`naming URL patterns
930-  <naming-url-patterns>` for information). We're also using the
931-  :func:`~django.conf.urls.url` function from
932-  :mod:`django.conf.urls` here. It's a good habit to use
933-  :func:`~django.conf.urls.url` when you are providing a
934-  pattern name like this.
935-
936 By default, the :class:`~django.views.generic.list.DetailView` generic
937 view uses a template called ``<app name>/<model name>_detail.html``.
938 In our case, it'll use the template ``"polls/poll_detail.html"``. The
939@@ -308,41 +270,13 @@ You can now delete the ``index()``, ``detail()`` and ``results()``
940 views from ``polls/views.py``. We don't need them anymore -- they have
941 been replaced by generic views.
942 
943-The last thing to do is fix the URL handling to account for the use of
944-generic views. In the vote view above, we used the
945-:func:`~django.core.urlresolvers.reverse` function to avoid
946-hard-coding our URLs. Now that we've switched to a generic view, we'll
947-need to change the :func:`~django.core.urlresolvers.reverse` call to
948-point back to our new generic view. We can't simply use the view
949-function anymore -- generic views can be (and are) used multiple times
950--- but we can use the name we've given::
951-
952-    return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))
953-
954-The same rule apply for the :ttag:`url` template tag. For example in the
955-``results.html`` template:
956-
957-.. code-block:: html+django
958-
959-    <a href="{% url 'poll_detail' poll.id %}">Vote again?</a>
960-
961 Run the server, and use your new polling app based on generic views.
962 
963 For full details on generic views, see the :doc:`generic views documentation
964 </topics/class-based-views/index>`.
965 
966-Coming soon
967-===========
968-
969-The tutorial ends here for the time being. Future installments of the tutorial
970-will cover:
971-
972-* Advanced form processing
973-* Using the RSS framework
974-* Using the cache framework
975-* Using the comments framework
976-* Advanced admin features: Permissions
977-* Advanced admin features: Custom JavaScript
978+What's next?
979+============
980 
981-In the meantime, you might want to check out some pointers on :doc:`where to go
982-from here </intro/whatsnext>`
983+The tutorial ends here for the time being. In the meantime, you might want to
984+check out some pointers on :doc:`where to go from here </intro/whatsnext>`.