Ticket #18715: 18715.2.diff
File 18715.2.diff, 43.4 KB (added by , 12 years ago) |
---|
-
docs/intro/tutorial02.txt
diff --git a/docs/intro/tutorial02.txt b/docs/intro/tutorial02.txt index fd13230..b87b280 100644
a b Open your settings file (``mysite/settings.py``, remember) and look at the 440 440 filesystem directories to check when loading Django templates. It's a search 441 441 path. 442 442 443 Create a ``mytemplates`` directory in your project directory. Templates can 444 live anywhere on your filesystem that Django can access. (Django runs as 445 whatever user your server runs.) However, keeping your templates within the 446 project is a good convention to follow. 447 448 When you’ve done that, create a directory polls in your template directory. 449 Within that, create a file called index.html. Note that our 450 ``loader.get_template('polls/index.html')`` code from above maps to 451 [template_directory]/polls/index.html” on the filesystem. 452 443 453 By default, :setting:`TEMPLATE_DIRS` is empty. So, let's add a line to it, to 444 454 tell Django where our templates live:: 445 455 446 456 TEMPLATE_DIRS = ( 447 '/ home/my_username/mytemplates', # Change this to your own directory.457 '/path/to/mysite/mytemplates', # Change this to your own directory. 448 458 ) 449 459 450 460 Now copy the template ``admin/base_site.html`` from within the default Django 451 461 admin template directory in the source code of Django itself 452 462 (``django/contrib/admin/templates``) into an ``admin`` subdirectory of 453 463 whichever directory you're using in :setting:`TEMPLATE_DIRS`. For example, if 454 your :setting:`TEMPLATE_DIRS` includes ``'/ home/my_username/mytemplates'``, as464 your :setting:`TEMPLATE_DIRS` includes ``'/path/to/mysite/mytemplates'``, as 455 465 above, then copy ``django/contrib/admin/templates/admin/base_site.html`` to 456 ``/ home/my_username/mytemplates/admin/base_site.html``. Don't forget that466 ``/path/to/mysite/mytemplates/admin/base_site.html``. Don't forget that 457 467 ``admin`` subdirectory. 458 468 459 469 .. admonition:: Where are the Django source files? -
docs/intro/tutorial03.txt
diff --git a/docs/intro/tutorial03.txt b/docs/intro/tutorial03.txt index f350102..169e6cd 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 126 Regular Expressions" by Jeffrey Friedl is fantastic. 130 Regular Expressions" by Jeffrey Friedl is fantastic. In practice, however, 131 you don't need to be an expert on regular expressions, as you really only need 132 to know how to capture simple patterns. In fact, complex regexes can have poor 133 lookup performance, so you probably shouldn't rely on the full power of regexes. 127 134 128 135 Finally, a performance note: these regular expressions are compiled the first 129 time the URLconf module is loaded. They're super fast. 136 time the URLconf module is loaded. They're super fast (as long as the lookups 137 aren't too complex as noted above). 130 138 131 139 .. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression 132 140 133 Write your firstview134 ===================== 141 :func:`~django.conf.urls.url` argument: view 142 -------------------------------------------- 135 143 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. 144 When Django finds a regular expression match, Django calls the specified view 145 function, with an :class:`~django.http.HttpRequest` object as the first 146 argument and any “captured” values from the regular expression as other 147 arguments. If the regex uses simple captures, values are passed as positional 148 arguments; if it uses named captures, values are passed as keyword arguments. 149 We'll give an example of this in a bit. 138 150 139 Fire up the Django development Web server: 151 :func:`~django.conf.urls.url` argument: kwargs 152 ---------------------------------------------- 140 153 141 .. code-block:: bash 154 Arbitrary keyword arguments can be passed in a dictionary to the target view. We 155 aren't going to use this feature of Django in the tutorial. 142 156 143 python manage.py runserver 157 :func:`~django.conf.urls.url` argument: name 158 --------------------------------------------- 144 159 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:: 160 Naming your URL lets you refer to it unambiguously from elsewhere in Django 161 especially templates. This powerful feature allows you to make global changes 162 to the url patterns of your project while only touching a single file. 147 163 148 ViewDoesNotExist at /polls/ 164 Writing more views 165 ================== 149 166 150 Could not import polls.views.index. View does not exist in module polls.views. 167 Now let's add a few more views to ``polls/views.py``. These views are 168 slightly different, because they take an argument:: 151 169 152 This error happened because you haven't written a function ``index()`` in the 153 module ``polls/views.py``. 170 def detail(request, poll_id): 171 return HttpResponse("You're looking at poll %s." % poll_id) 154 172 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). 173 def results(request, poll_id): 174 return HttpResponse("You're looking at the results of poll %s." % poll_id) 158 175 159 Time to write the first view. Open the file ``polls/views.py`` 160 and put the following Python code in it:: 176 def vote(request, poll_id): 177 return HttpResponse("You're voting on poll %s." % poll_id) 161 178 162 from django.http import HttpResponse 179 Wire these news views into the ``polls.urls`` module by adding the following 180 :func:`~django.conf.urls.url` calls:: 163 181 164 def index(request): 165 return HttpResponse("Hello, world. You're at the poll index.") 182 from django.conf.urls import patterns, url 166 183 167 This is the simplest view possible. Go to "/polls/" in your browser, and you 168 should see your text. 184 from polls import views 169 185 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):: 186 urlpatterns = patterns('', 187 # ex: /polls/ 188 url(r'^$', views.index, name='index'), 189 # ex: /polls/5/ 190 url(r'^(?P<poll_id>\d+)/$', views.detail, name='detail'), 191 # ex: /polls/5/results/ 192 url(r'^(?P<poll_id>\d+)/results/$', views.results, name='results'), 193 # ex: /polls/5/vote/ 194 url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'), 195 ) 173 196 174 def detail(request, poll_id): 175 return HttpResponse("You're looking at poll %s." % poll_id) 197 Take a look in your browser, at "/polls/34/". It'll run the ``detail()`` 198 method and display whatever ID you provide in the URL. Try 199 "/polls/34/results/" and "/polls/34/vote/" too -- these will display the 200 placeholder results and voting pages. 201 202 When somebody requests a page from your Web site -- say, "/polls/34/", Django 203 will load the ``mysite.urls`` Python module because it's pointed to by the 204 :setting:`ROOT_URLCONF` setting. It finds the variable named ``urlpatterns`` 205 and traverses the regular expressions in order. The 206 :func:`~django.conf.urls.include` functions we are using simply reference 207 other URLconfs. Note that the regular expressions for the 208 :func:`~django.conf.urls.include` functions don't have a ``$`` (end-of-string 209 match character) but rather a trailing slash. Whenever Django encounters 210 :func:`~django.conf.urls.include`, it chops off whatever part of the URL 211 matched up to that point and sends the remaining string to the included 212 URLconf for further processing. 176 213 177 def results(request, poll_id): 178 return HttpResponse("You're looking at the results of poll %s." % poll_id) 214 The idea behind :func:`~django.conf.urls.include` is to make it easy to 215 plug-and-play URLs. Since polls are in their own URLconf 216 (``polls/urls.py``), they can be placed under "/polls/", or under 217 "/fun_polls/", or under "/content/polls/", or any other path root, and the 218 app will still work. 179 219 180 def vote(request, poll_id): 181 return HttpResponse("You're voting on poll %s." % poll_id) 220 Here's what happens if a user goes to "/polls/34/" in this system: 182 221 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. 222 * Django will find the match at ``'^polls/'`` 223 224 * Then, Django will strip off the matching text (``"polls/"``) and send the 225 remaining text -- ``"34/"`` -- to the 'polls.urls' URLconf for 226 further processing which matches ``r'^(?P<poll_id>\d+)/$'`` resulting in a 227 call to the ``detail()`` view like so:: 228 229 detail(request=<HttpRequest object>, poll_id='34') 230 231 The ``poll_id='34'`` part comes from ``(?P<poll_id>\d+)``. Using parentheses 232 around a pattern "captures" the text matched by that pattern and sends it as an 233 argument to the view function; ``?P<poll_id>`` defines the name that will 234 be used to identify the matched pattern; and ``\d+`` is a regular expression to 235 match a sequence of digits (i.e., a number). 236 237 Because the URL patterns are regular expressions, there really is no limit on 238 what you can do with them. And there's no need to add URL cruft such as ``.php`` 239 -- unless you have a sick sense of humor, in which case you can do something 240 like this:: 241 242 (r'^polls/latest\.php$', 'polls.views.index'), 243 244 But, don't do that. It's silly. 187 245 188 246 Write views that actually do something 189 247 ====================================== 190 248 191 Each view is responsible for doing one of two things: Returning an249 Each view is responsible for doing one of two things: returning an 192 250 :class:`~django.http.HttpResponse` object containing the content for the 193 251 requested page, or raising an exception such as :exc:`~django.http.Http404`. The 194 252 rest is up to you. … … in :doc:`Tutorial 1 </intro/tutorial01>`. Here's one stab at the ``index()`` 205 263 view, which displays the latest 5 poll questions in the system, separated by 206 264 commas, according to publication date:: 207 265 208 from polls.models import Poll209 266 from django.http import HttpResponse 210 267 268 from polls.models import Poll 269 211 270 def index(request): 212 latest_poll_list = Poll.objects. all().order_by('-pub_date')[:5]271 latest_poll_list = Poll.objects.order_by('-pub_date')[:5] 213 272 output = ', '.join([p.question for p in latest_poll_list]) 214 273 return HttpResponse(output) 215 274 216 There's a problem here, though: The page's design is hard-coded in the view. If275 There's a problem here, though: the page's design is hard-coded in the view. If 217 276 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:: 219 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 239 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. 277 So let's use Django's template system to separate the design from Python. 252 278 279 First, create a directory ``polls`` in your template directory you specified 280 in setting:`TEMPLATE_DIRS`. Within that, create a file called ``index.html``. 253 281 Put the following code in that template: 254 282 255 283 .. code-block:: html+django … … Put the following code in that template: 264 292 <p>No polls are available.</p> 265 293 {% endif %} 266 294 295 Now let's use that html template in our index view:: 296 297 from django.http import HttpResponse 298 from django.template import Context, loader 299 300 from polls.models import Poll 301 302 def index(request): 303 latest_poll_list = Poll.objects.order_by('-pub_date')[:5] 304 template = loader.get_template('polls/index.html') 305 context = Context({ 306 'latest_poll_list': latest_poll_list, 307 }) 308 return HttpResponse(template.render(context)) 309 310 That code loads the template called ``polls/index.html`` and passes it a 311 context. The context is a dictionary mapping template variable names to Python 312 objects. 313 267 314 Load the page in your Web browser, and you should see a bulleted-list 268 315 containing the "What's up" poll from Tutorial 1. The link points to the poll's 269 316 detail page. 270 317 271 A shortcut: render_to_response()272 -------------------------------- 318 A shortcut: :func:`~django.shortcuts.render` 319 -------------------------------------------- 273 320 274 321 It's a very common idiom to load a template, fill a context and return an 275 322 :class:`~django.http.HttpResponse` object with the result of the rendered 276 323 template. Django provides a shortcut. Here's the full ``index()`` view, 277 324 rewritten:: 278 325 279 from django.shortcuts import render_to_response 326 from django.shortcuts import render 327 280 328 from polls.models import Poll 281 329 282 330 def index(request): 283 331 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}) 332 context = {'latest_poll_list': latest_poll_list} 333 return render(request, 'polls/index.html', context) 285 334 286 335 Note that once we've done this in all these views, we no longer need to import 287 336 :mod:`~django.template.loader`, :class:`~django.template.Context` and 288 :class:`~django.http.HttpResponse`. 337 :class:`~django.http.HttpResponse` (you'll want to keep ``HttpResponse`` if you 338 still have the stub methods for ``detail``, ``results``, and ``vote``). 289 339 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.340 The :func:`~django.shortcuts.render` function takes the request object as its 341 first argument, a template name as its second argument and a dictionary as its 342 optional third argument. It returns an :class:`~django.http.HttpResponse` 343 object of the given template rendered with the given context. 294 344 295 Raising 404296 =========== 345 Raising a 404 error 346 =================== 297 347 298 348 Now, let's tackle the poll detail view -- the page that displays the question 299 349 for a given poll. Here's the view:: … … for a given poll. Here's the view:: 302 352 # ... 303 353 def detail(request, poll_id): 304 354 try: 305 p = Poll.objects.get(pk=poll_id)355 poll = Poll.objects.get(pk=poll_id) 306 356 except Poll.DoesNotExist: 307 357 raise Http404 308 return render _to_response('polls/detail.html', {'poll': p})358 return render(request, 'polls/detail.html', {'poll': poll}) 309 359 310 360 The new concept here: The view raises the :exc:`~django.http.Http404` exception 311 361 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 367 318 368 will get you started for now. 319 369 320 A shortcut: get_object_or_404()321 ------------------------------- 370 A shortcut: :func:`~django.shortcuts.get_object_or_404` 371 ------------------------------------------------------- 322 372 323 373 It's a very common idiom to use :meth:`~django.db.models.query.QuerySet.get` 324 374 and raise :exc:`~django.http.Http404` if the object doesn't exist. Django 325 375 provides a shortcut. Here's the ``detail()`` view, rewritten:: 326 376 327 from django.shortcuts import render _to_response, get_object_or_404377 from django.shortcuts import render, get_object_or_404 328 378 # ... 329 379 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})380 poll = get_object_or_404(Poll, pk=poll_id) 381 return render(request, 'polls/detail.html', {'poll': poll}) 332 382 333 383 The :func:`~django.shortcuts.get_object_or_404` function takes a Django model 334 384 as its first argument and an arbitrary number of keyword arguments, which it … … exist. 345 395 :exc:`~django.core.exceptions.ObjectDoesNotExist`? 346 396 347 397 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. 398 foremost design goals of Django is to maintain loose coupling. Some 399 controlled coupling is introduced in the :mod:`django.shortcuts` module. 349 400 350 401 There's also a :func:`~django.shortcuts.get_list_or_404` function, which works 351 402 just as :func:`~django.shortcuts.get_object_or_404` -- except using … … You normally won't have to bother with writing 404 views. If you don't set 369 420 is used by default. Optionally, you can create a ``404.html`` template 370 421 in the root of your template directory. The default 404 view will then use that 371 422 template for all 404 errors when :setting:`DEBUG` is set to ``False`` (in your 372 settings module). 423 settings module). If you do create the template, add at least some dummy 424 content like "Page not found". 373 425 374 426 A couple more things to note about 404 views: 375 427 … … Similarly, your root URLconf may define a ``handler500``, which points 387 439 to a view to call in case of server errors. Server errors happen when 388 440 you have runtime errors in view code. 389 441 442 Likewise, you should create a ``500.html`` template at the root of your 443 template directory and add some content like "Something went wrong". 444 390 445 Use the template system 391 446 ======================= 392 447 393 448 Back to the ``detail()`` view for our poll application. Given the context 394 variable ``poll``, here's what the "polls/detail.html"template might look449 variable ``poll``, here's what the ``polls/detail.html`` template might look 395 450 like: 396 451 397 452 .. code-block:: html+django … … suitable for use in the :ttag:`{% for %}<for>` tag. 416 471 417 472 See the :doc:`template guide </topics/templates>` for more about templates. 418 473 419 Simplifying the URLconfs420 ======================== 474 Removing hardcoded URLs in templates 475 ==================================== 421 476 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::477 Remember, when we wrote the link to a poll in the ``polls/index.html`` 478 template, the link was partially hardcoded like this: 424 479 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 ) 480 .. code-block:: html+django 431 481 432 Namely, ``polls.views`` is in every callback. 482 <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li> 433 483 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:: 484 The problem with this hardcoded, tightly-coupled approach is that it becomes 485 challenging to change URLs on projects with a lot of templates. However, since 486 you defined the name argument in the :func:`~django.conf.urls.url` functions in 487 the ``polls.urls`` module, you can remove a reliance on specific URL paths 488 defined in your url configurations by using the ``{% url %}`` template tag: 437 489 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 ) 490 .. code-block:: html+django 444 491 445 This is functionally identical to the previous formatting. It's just a bit 446 tidier. 492 <li><a href="{% url 'detail' poll.id %}">{{ poll.question }}</a></li> 447 493 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:: 494 .. note:: 452 495 453 from django.conf.urls import patterns, include, url 496 If ``{% url 'detail' poll.id %}`` (with quotes) doesn't work, but 497 ``{% url detail poll.id %}`` (without quotes) does, that means you're 498 using a version of Django < 1.5. In this case, add the following 499 declaration at the top of your template: 454 500 455 from django.contrib import admin 456 admin.autodiscover() 501 .. code-block:: html+django 457 502 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 ) 503 {% load url from future %} 464 504 465 urlpatterns += patterns('', 466 url(r'^admin/', include(admin.site.urls)), 467 ) 505 The way this works is by looking up the URL definition as specified in the 506 ``polls.urls`` module. You can see exactly where the URL name of 'detail' is 507 defined below:: 468 508 469 Decoupling the URLconfs 470 ======================= 509 ... 510 # the 'name' value as called by the {% url %} template tag 511 url(r'^(?P<poll_id>\d+)/$', views.detail, name='detail'), 512 ... 471 513 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. 514 If you want to change the URL of the polls detail view to something else, 515 perhaps to something like ``polls/specifics/12/`` instead of doing it in the 516 template (or templates) you would change it in ``polls/urls.py``:: 476 517 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. 518 ... 519 # added the word 'specifics' 520 url(r'^specifics/(?P<poll_id>\d+)/$', views.detail, name='detail'), 521 ... 480 522 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. 523 Namespacing URL names 524 ====================== 484 525 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:: 526 The tutorial project has just one app, ``polls``. In real Django projects, 527 there might be five, ten, twenty apps or more. How does Django differentiate 528 the URL names between them? For example, the ``polls`` app has a ``detail`` 529 view, and so might an app on the same project that is for a blog. How does one 530 make it so that Django knows which app view to create for a url when using the 531 ``{% url %}`` template tag? 532 533 The answer is to add namespaces to your root URLconf. In the 534 ``mysite/urls.py`` file, go ahead and change it to include namespacing:: 488 535 489 536 from django.conf.urls import patterns, include, url 490 537 … … Copy the file ``mysite/urls.py`` to ``polls/urls.py``. Then, change 492 539 admin.autodiscover() 493 540 494 541 urlpatterns = patterns('', 495 url(r'^polls/', include('polls.urls' )),542 url(r'^polls/', include('polls.urls', namespace="polls")), 496 543 url(r'^admin/', include(admin.site.urls)), 497 544 ) 498 545 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: 546 Now change your ``polls/index.html`` template from: 541 547 542 548 .. code-block:: html+django 543 549 544 <li><a href=" /polls/{{ poll.id }}/">{{ poll.question }}</a></li>550 <li><a href="{% url 'detail' poll.id %}">{{ poll.question }}</a></li> 545 551 546 To use the decoupled URLs we've just introduced, replace the hardcoded link 547 with the :ttag:`url` template tag: 552 to point at the namespaced detail view: 548 553 549 554 .. code-block:: html+django 550 555 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 %} 556 <li><a href="{% url 'polls:detail' poll.id %}">{{ poll.question }}</a></li> 563 557 564 558 When you're comfortable with writing views, read :doc:`part 4 of this tutorial 565 559 </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>`.