Ticket #18715: 18715.diff
File 18715.diff, 40.9 KB (added by , 12 years ago) |
---|
-
docs/intro/tutorial03.txt
diff --git a/docs/intro/tutorial03.txt b/docs/intro/tutorial03.txt index f350102..3031165 100644
a b Philosophy 10 10 ========== 11 11 12 12 A 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 Weblog13 a specific function and has a specific template. For example, in a blog 14 14 application, you might have the following views: 15 15 16 16 * Blog homepage -- displays the latest few entries. … … In our poll application, we'll have the following four views: 41 41 42 42 In Django, each view is represented by a simple Python function. 43 43 44 Design your URLs 45 ================ 44 Write your first view 45 ===================== 46 47 Let's write the first view. Open the file ``polls/views.py`` 48 and 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.") 46 54 47 Th e first step of writing views is to design your URL structure. You do this by48 creating a Python module, called a URLconf. URLconfs are how Django associates49 a given URL with given Python code.55 This is the simplest view possible in Django. Now we have a problem, how does 56 this view get called? For that we need to map it to a URL, in Django this is 57 done in a configuration file called a URLconf. 50 58 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? 55 60 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. 57 68 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. 69 To create a URLconf in the polls directory, create a file called ``urls.py``. 70 Your app directory should now look like:: 61 71 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 67 79 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`. 80 In the ``polls/urls.py`` file include the following code:: 71 81 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 76 83 77 ROOT_URLCONF = 'mysite.urls'84 from polls import views 78 85 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 90 The 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 92 with:: 80 93 81 94 from django.conf.urls import patterns, include, url 82 95 … … Time for an example. Edit ``mysite/urls.py`` so it looks like this:: 84 97 admin.autodiscover() 85 98 86 99 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')), 91 101 url(r'^admin/', include(admin.site.urls)), 92 102 ) 93 103 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:: 104 You have now wired an `index` view into the URLconf. Go to 105 http://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. 101 108 102 detail(request=<HttpRequest object>, poll_id='23') 109 The :func:`~django.conf.urls.url` function is passed four arguments, two 110 required: ``regex`` and ``view``, and two optional: ``kwargs``, and ``name``. 111 At this point, it's worth reviewing what these arguments are for. 103 112 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 --------------------------------------------- 109 115 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. 116 The term `regex` is a commonly used short form meaning `regular expression`, 117 which is a syntax for matching patterns in strings, or in this case, url 118 patterns. Django starts at the first regular expression and makes its way down 119 the list, comparing the requested URL against each regular expression until it 120 finds one that matches. 118 121 119 122 Note 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/``. 123 the domain name. For example, in a request to 124 ``http://www.example.com/myapp/``, the URLconf will look for ``myapp/``. In a 125 request to ``http://www.example.com/myapp/?page=3``, the URLconf will also 126 look for ``myapp/``. 123 127 124 128 If you need help with regular expressions, see `Wikipedia's entry`_ and the 125 129 documentation of the :mod:`re` module. Also, the O'Reilly book "Mastering … … time the URLconf module is loaded. They're super fast. 130 134 131 135 .. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression 132 136 133 Write your firstview134 ===================== 137 :func:`~django.conf.urls.url` argument: view 138 -------------------------------------------- 135 139 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. 140 When Django finds a regular expression match, Django calls the specified view 141 function, with an :class:`~django.http.HttpRequest` object as the first 142 argument and any “captured” values from the regular expression as other 143 arguments. If the regex uses simple captures, values are passed as positional 144 arguments; if it uses named captures, values are passed as keyword arguments. 145 We'll give an example of this in a bit. 138 146 139 Fire up the Django development Web server: 147 :func:`~django.conf.urls.url` argument: kwargs 148 ---------------------------------------------- 140 149 141 .. code-block:: bash 150 Arbitrary keyword arguments can be passed in a dictionary to the target view. We 151 aren't going to use this feature of Django in the tutorial. 142 152 143 python manage.py runserver 153 :func:`~django.conf.urls.url` argument: name 154 --------------------------------------------- 144 155 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:: 156 Naming your URL lets you refer to it unambiguously from elsewhere in Django 157 especially templates. This powerful feature allows you to make global changes 158 to the url patterns of your project while only touching a single file. 147 159 148 ViewDoesNotExist at /polls/ 160 Writing more views 161 ================== 149 162 150 Could not import polls.views.index. View does not exist in module polls.views. 163 Now let's add a few more views to ``polls/views.py``. These views are 164 slightly different, because they take an argument:: 151 165 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) 154 168 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) 158 171 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) 161 174 162 from django.http import HttpResponse 175 Wire these news views into the ``polls.urls`` module by adding the following 176 :func:`~django.conf.urls.url` calls:: 163 177 164 def index(request): 165 return HttpResponse("Hello, world. You're at the poll index.") 178 from django.conf.urls import patterns, url 166 179 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 169 181 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 ) 173 192 174 def detail(request, poll_id): 175 return HttpResponse("You're looking at poll %s." % poll_id) 193 Take a look in your browser, at "/polls/34/". It'll run the ``detail()`` 194 method and display whatever ID you provide in the URL. Try 195 "/polls/34/results/" and "/polls/34/vote/" too -- these will display the 196 placeholder results and voting pages. 197 198 When somebody requests a page from your Web site -- say, "/polls/34/", Django 199 will load the ``mysite.urls`` Python module because it's pointed to by the 200 :setting:`ROOT_URLCONF` setting. It finds the variable named ``urlpatterns`` 201 and traverses the regular expressions in order. The 202 :func:`~django.conf.urls.include` functions we are using simply reference 203 other URLconfs. Note that the regular expressions for the 204 :func:`~django.conf.urls.include` functions don't have a ``$`` (end-of-string 205 match character) but rather a trailing slash. Whenever Django encounters 206 :func:`~django.conf.urls.include`, it chops off whatever part of the URL 207 matched up to that point and sends the remaining string to the included 208 URLconf for further processing. 176 209 177 def results(request, poll_id): 178 return HttpResponse("You're looking at the results of poll %s." % poll_id) 210 The idea behind :func:`~django.conf.urls.include` is to make it easy to 211 plug-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 214 app will still work. 179 215 180 def vote(request, poll_id): 181 return HttpResponse("You're voting on poll %s." % poll_id) 216 Here's what happens if a user goes to "/polls/34/" in this system: 182 217 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 227 The ``poll_id='34'`` part comes from ``(?P<poll_id>\d+)``. Using parentheses 228 around a pattern "captures" the text matched by that pattern and sends it as an 229 argument to the view function; ``?P<poll_id>`` defines the name that will 230 be used to identify the matched pattern; and ``\d+`` is a regular expression to 231 match a sequence of digits (i.e., a number). 232 233 Because the URL patterns are regular expressions, there really is no limit on 234 what 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 236 like this:: 237 238 (r'^polls/latest\.php$', 'polls.views.index'), 239 240 But, don't do that. It's silly. 187 241 188 242 Write views that actually do something 189 243 ====================================== 190 244 191 Each view is responsible for doing one of two things: Returning an245 Each view is responsible for doing one of two things: returning an 192 246 :class:`~django.http.HttpResponse` object containing the content for the 193 247 requested page, or raising an exception such as :exc:`~django.http.Http404`. The 194 248 rest is up to you. … … in :doc:`Tutorial 1 </intro/tutorial01>`. Here's one stab at the ``index()`` 205 259 view, which displays the latest 5 poll questions in the system, separated by 206 260 commas, according to publication date:: 207 261 208 from polls.models import Poll209 262 from django.http import HttpResponse 210 263 264 from polls.models import Poll 265 211 266 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] 213 268 output = ', '.join([p.question for p in latest_poll_list]) 214 269 return HttpResponse(output) 215 270 216 There's a problem here, though: The page's design is hard-coded in the view. If271 There's a problem here, though: the page's design is hard-coded in the view. If 217 272 you 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 ::273 So let's use Django's template system to separate the design from Python. 219 274 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 275 First, create a directory, somewhere on your filesystem, whose contents Django 276 can access. (Django runs as whatever user your server runs.) Don't put them 277 under your document root, though. You probably shouldn't make them public, just 278 for security's sake. Then edit :setting:`TEMPLATE_DIRS` in your ``settings.py`` 279 to tell Django where it can find templates -- just as you did in the "Customize 280 the admin look and feel" section of Tutorial 2. 239 281 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: 282 When you've done that, create a directory ``polls`` in your template 283 directory. Within that, create a file called ``index.html``. Put the following 284 code in that template: 254 285 255 286 .. code-block:: html+django 256 287 … … Put the following code in that template: 264 295 <p>No polls are available.</p> 265 296 {% endif %} 266 297 298 Now 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 313 That code loads the template called ``polls/index.html`` and passes it a 314 context. The context is a dictionary mapping template variable names to Python 315 objects. 316 267 317 Load the page in your Web browser, and you should see a bulleted-list 268 318 containing the "What's up" poll from Tutorial 1. The link points to the poll's 269 319 detail page. 270 320 271 A shortcut: render_to_response()272 -------------------------------- 321 A shortcut: :func:`~django.shortcuts.render` 322 -------------------------------------------- 273 323 274 324 It's a very common idiom to load a template, fill a context and return an 275 325 :class:`~django.http.HttpResponse` object with the result of the rendered 276 326 template. Django provides a shortcut. Here's the full ``index()`` view, 277 327 rewritten:: 278 328 279 from django.shortcuts import render_to_response 329 from django.shortcuts import render 330 280 331 from polls.models import Poll 281 332 282 333 def index(request): 283 334 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) 285 337 286 338 Note that once we've done this in all these views, we no longer need to import 287 339 :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 341 still have the stub methods for ``detail``, ``results``, and ``vote``). 289 342 290 The :func:`~django.shortcuts.render _to_response` function takes a template name291 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.343 The :func:`~django.shortcuts.render` function takes the request object as its 344 first argument, a template name as its second argument and a dictionary as its 345 optional third argument. It returns an :class:`~django.http.HttpResponse` 346 object of the given template rendered with the given context. 294 347 295 Raising 404296 =========== 348 Raising a 404 error 349 =================== 297 350 298 351 Now, let's tackle the poll detail view -- the page that displays the question 299 352 for a given poll. Here's the view:: … … for a given poll. Here's the view:: 302 355 # ... 303 356 def detail(request, poll_id): 304 357 try: 305 p = Poll.objects.get(pk=poll_id)358 poll = Poll.objects.get(pk=poll_id) 306 359 except Poll.DoesNotExist: 307 360 raise Http404 308 return render _to_response('polls/detail.html', {'poll': p})361 return render(request, 'polls/detail.html', {'poll': poll}) 309 362 310 363 The new concept here: The view raises the :exc:`~django.http.Http404` exception 311 364 if a poll with the requested ID doesn't exist. … … later, but if you'd like to quickly get the above example working, just:: 317 370 318 371 will get you started for now. 319 372 320 A shortcut: get_object_or_404()321 ------------------------------- 373 A shortcut: :func:`~django.shortcuts.get_object_or_404` 374 ------------------------------------------------------- 322 375 323 376 It's a very common idiom to use :meth:`~django.db.models.query.QuerySet.get` 324 377 and raise :exc:`~django.http.Http404` if the object doesn't exist. Django 325 378 provides a shortcut. Here's the ``detail()`` view, rewritten:: 326 379 327 from django.shortcuts import render _to_response, get_object_or_404380 from django.shortcuts import render, get_object_or_404 328 381 # ... 329 382 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}) 332 385 333 386 The :func:`~django.shortcuts.get_object_or_404` function takes a Django model 334 387 as its first argument and an arbitrary number of keyword arguments, which it … … exist. 345 398 :exc:`~django.core.exceptions.ObjectDoesNotExist`? 346 399 347 400 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. 349 403 350 404 There's also a :func:`~django.shortcuts.get_list_or_404` function, which works 351 405 just as :func:`~django.shortcuts.get_object_or_404` -- except using … … Similarly, your root URLconf may define a ``handler500``, which points 387 441 to a view to call in case of server errors. Server errors happen when 388 442 you have runtime errors in view code. 389 443 444 Likewise, you should create a ``500.html`` template at the root of your 445 template directory. 446 390 447 Use the template system 391 448 ======================= 392 449 393 450 Back to the ``detail()`` view for our poll application. Given the context 394 variable ``poll``, here's what the "polls/detail.html"template might look451 variable ``poll``, here's what the ``polls/detail.html`` template might look 395 452 like: 396 453 397 454 .. code-block:: html+django … … suitable for use in the :ttag:`{% for %}<for>` tag. 416 473 417 474 See the :doc:`template guide </topics/templates>` for more about templates. 418 475 419 Simplifying the URLconfs420 ======================== 476 Removing hardcoded URLs in templates 477 ==================================== 421 478 422 Take some time to play around with the views and template system. As you edit 423 t he URLconf, you may notice there's a fair bit of redundancy in it::479 Remember, when we wrote the link to a poll in the ``polls/index.html`` 480 template, the link was partially hardcoded like this: 424 481 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 431 483 432 Namely, ``polls.views`` is in every callback. 484 <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li> 433 485 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:: 486 The problem with this hardcoded, tightly-coupled approach is that it becomes 487 challenging to change URLs on projects with a lot of templates. However, since 488 you defined the name argument in the :func:`~django.conf.urls.url` functions in 489 the ``polls.urls`` module, you can remove a reliance on specific URL paths 490 defined in your url configurations by using the ``{% url %}`` template tag: 437 491 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 444 493 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> 447 495 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:: 452 497 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: 454 502 455 from django.contrib import admin 456 admin.autodiscover() 503 .. code-block:: html+django 457 504 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 %} 464 506 465 urlpatterns += patterns('', 466 url(r'^admin/', include(admin.site.urls)), 467 ) 507 The 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 509 defined below:: 468 510 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 516 If you want to change the URL of the polls detail view to something else, 517 perhaps to something like ``polls/specifics/12/`` instead of doing it in the 518 template (or templates) you would change it in ``polls/urls.py``:: 471 519 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 ... 476 524 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. 525 Namespacing URL names 526 ====================== 480 527 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. 528 The tutorial project has just one app, ``polls``. In real Django projects, 529 there might be five, ten, twenty apps or more. How does Django differentiate 530 the URL names between them? For example, the ``polls`` app has a ``detail`` 531 view, and so might an app on the same project that is for a blog. How does one 532 make it so that Django knows which app view to create for a url when using the 533 ``{% url %}`` template tag? 484 534 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:: 535 The answer is to add namespaces to your root URLconf. In the 536 ``mysite/urls.py`` file, go ahead and change it to include namespacing:: 488 537 489 538 from django.conf.urls import patterns, include, url 490 539 … … Copy the file ``mysite/urls.py`` to ``polls/urls.py``. Then, change 492 541 admin.autodiscover() 493 542 494 543 urlpatterns = patterns('', 495 url(r'^polls/', include('polls.urls' )),544 url(r'^polls/', include('polls.urls', namespace="polls")), 496 545 url(r'^admin/', include(admin.site.urls)), 497 546 ) 498 547 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: 548 Now change your ``polls/index.html`` template from: 541 549 542 550 .. code-block:: html+django 543 551 544 <li><a href=" /polls/{{ poll.id }}/">{{ poll.question }}</a></li>552 <li><a href="{% url 'detail' poll.id %}">{{ poll.question }}</a></li> 545 553 546 To use the decoupled URLs we've just introduced, replace the hardcoded link 547 with the :ttag:`url` template tag: 554 to point at the namespaced detail view: 548 555 549 556 .. code-block:: html+django 550 557 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> 563 559 564 560 When you're comfortable with writing views, read :doc:`part 4 of this tutorial 565 561 </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: 18 18 19 19 {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} 20 20 21 <form action="{% url 'polls .views.vote' poll.id %}" method="post">21 <form action="{% url 'polls:vote' poll.id %}" method="post"> 22 22 {% csrf_token %} 23 23 {% for choice in poll.choice_set.all %} 24 24 <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" /> … … A quick rundown: 35 35 selects one of the radio buttons and submits the form, it'll send the 36 36 POST data ``choice=3``. This is HTML Forms 101. 37 37 38 * We set the form's ``action`` to ``{% url 'polls .views.vote' poll.id %}``, and we38 * We set the form's ``action`` to ``{% url 'polls:vote' poll.id %}``, and we 39 39 set ``method="post"``. Using ``method="post"`` (as opposed to 40 40 ``method="get"``) is very important, because the act of submitting this 41 41 form will alter data server-side. Whenever you create a form that alters … … A quick rundown: 52 52 forms that are targeted at internal URLs should use the 53 53 :ttag:`{% csrf_token %}<csrf_token>` template tag. 54 54 55 The :ttag:`{% csrf_token %}<csrf_token>` tag requires information from the56 request object, which is not normally accessible from within the template57 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 RequestContext61 # ...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 for68 :ref:`RequestContext <subclassing-context-requestcontext>`.69 70 55 Now, let's create a Django view that handles the submitted data and does 71 56 something with it. Remember, in :doc:`Tutorial 3 </intro/tutorial03>`, we 72 57 created a URLconf for the polls application that includes this line:: 73 58 74 (r'^(?P<poll_id>\d+)/vote/$','vote'),59 url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'), 75 60 76 61 We also created a dummy implementation of the ``vote()`` function. Let's 77 62 create a real version. Add the following to ``polls/views.py``:: 78 63 79 from django.shortcuts import get_object_or_404, render _to_response64 from django.shortcuts import get_object_or_404, render 80 65 from django.http import HttpResponseRedirect, HttpResponse 81 66 from django.core.urlresolvers import reverse 82 from django.template import RequestContext83 67 from polls.models import Choice, Poll 84 68 # ... 85 69 def vote(request, poll_id): … … create a real version. Add the following to ``polls/views.py``:: 88 72 selected_choice = p.choice_set.get(pk=request.POST['choice']) 89 73 except (KeyError, Choice.DoesNotExist): 90 74 # Redisplay the poll voting form. 91 return render _to_response('polls/detail.html', {75 return render(request, 'polls/detail.html', { 92 76 'poll': p, 93 77 'error_message': "You didn't select a choice.", 94 } , context_instance=RequestContext(request))78 }) 95 79 else: 96 80 selected_choice.votes += 1 97 81 selected_choice.save() 98 82 # Always return an HttpResponseRedirect after successfully dealing 99 83 # with POST data. This prevents data from being posted twice if a 100 84 # user hits the Back button. 101 return HttpResponseRedirect(reverse('polls .views.results', args=(p.id,)))85 return HttpResponseRedirect(reverse('polls:results', args=(p.id,))) 102 86 103 87 This code includes a few things we haven't covered yet in this tutorial: 104 88 … … This code includes a few things we haven't covered yet in this tutorial: 142 126 '/polls/3/results/' 143 127 144 128 ... 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. 147 130 148 131 As mentioned in Tutorial 3, ``request`` is a :class:`~django.http.HttpRequest` 149 132 object. For more on :class:`~django.http.HttpRequest` objects, see the … … After somebody votes in a poll, the ``vote()`` view redirects to the results 153 136 page for the poll. Let's write that view:: 154 137 155 138 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}) 158 141 159 142 This is almost exactly the same as the ``detail()`` view from :doc:`Tutorial 3 160 143 </intro/tutorial03>`. The only difference is the template name. We'll fix this 161 144 redundancy later. 162 145 163 Now, create a `` results.html`` template:146 Now, create a ``polls/results.html`` template: 164 147 165 148 .. code-block:: html+django 166 149 … … Now, create a ``results.html`` template: 172 155 {% endfor %} 173 156 </ul> 174 157 175 <a href="{% url 'polls .views.detail' poll.id %}">Vote again?</a>158 <a href="{% url 'polls:detail' poll.id %}">Vote again?</a> 176 159 177 160 Now, go to ``/polls/1/`` in your browser and vote in the poll. You should see a 178 161 results page that gets updated each time you vote. If you submit the form … … Read on for details. 215 198 216 199 You should know basic math before you start using a calculator. 217 200 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:: 201 First, open the ``polls/urls.py`` URLconf and change it like so:: 231 202 232 203 from django.conf.urls import patterns, url 233 204 from django.views.generic import DetailView, ListView … … Change it like so:: 239 210 queryset=Poll.objects.order_by('-pub_date')[:5], 240 211 context_object_name='latest_poll_list', 241 212 template_name='polls/index.html'), 242 name=' poll_index'),213 name='index'), 243 214 url(r'^(?P<pk>\d+)/$', 244 215 DetailView.as_view( 245 216 model=Poll, 246 217 template_name='polls/detail.html'), 247 name=' poll_detail'),218 name='detail'), 248 219 url(r'^(?P<pk>\d+)/results/$', 249 220 DetailView.as_view( 250 221 model=Poll, 251 222 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'), 254 225 ) 255 226 256 227 We're using two generic views here: … … two views abstract the concepts of "display a list of objects" and 267 238 ``"pk"``, so we've changed ``poll_id`` to ``pk`` for the generic 268 239 views. 269 240 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 the272 documentation about :ref:`naming URL patterns273 <naming-url-patterns>` for information). We're also using the274 :func:`~django.conf.urls.url` function from275 :mod:`django.conf.urls` here. It's a good habit to use276 :func:`~django.conf.urls.url` when you are providing a277 pattern name like this.278 279 241 By default, the :class:`~django.views.generic.list.DetailView` generic 280 242 view uses a template called ``<app name>/<model name>_detail.html``. 281 243 In our case, it'll use the template ``"polls/poll_detail.html"``. The … … You can now delete the ``index()``, ``detail()`` and ``results()`` 308 270 views from ``polls/views.py``. We don't need them anymore -- they have 309 271 been replaced by generic views. 310 272 311 The last thing to do is fix the URL handling to account for the use of312 generic views. In the vote view above, we used the313 :func:`~django.core.urlresolvers.reverse` function to avoid314 hard-coding our URLs. Now that we've switched to a generic view, we'll315 need to change the :func:`~django.core.urlresolvers.reverse` call to316 point back to our new generic view. We can't simply use the view317 function anymore -- generic views can be (and are) used multiple times318 -- 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 the323 ``results.html`` template:324 325 .. code-block:: html+django326 327 <a href="{% url 'poll_detail' poll.id %}">Vote again?</a>328 329 273 Run the server, and use your new polling app based on generic views. 330 274 331 275 For full details on generic views, see the :doc:`generic views documentation 332 276 </topics/class-based-views/index>`. 333 277 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 278 What's next? 279 ============ 346 280 347 In the meantime, you might want to check out some pointers on :doc:`where to go348 from here </intro/whatsnext>` 281 The tutorial ends here for the time being. In the meantime, you might want to 282 check out some pointers on :doc:`where to go from here </intro/whatsnext>`.