| | 1 | .. _topics-generic-views: |
| | 2 | |
| | 3 | ============= |
| | 4 | Generic views |
| | 5 | ============= |
| | 6 | |
| | 7 | Writing Web applications can be monotonous, because we repeat certain patterns |
| | 8 | again and again. Django tries to take away some of that monotony at the model |
| | 9 | and template layers, but Web developers also experience this boredom at the view |
| | 10 | level. |
| | 11 | |
| | 12 | Django's *generic views* were developed to ease that pain. They take certain |
| | 13 | common idioms and patterns found in view development and abstract them so that |
| | 14 | you can quickly write common views of data without having to write too much |
| | 15 | code. |
| | 16 | |
| | 17 | We can recognize certain common tasks, like displaying a list of objects, and |
| | 18 | write code that displays a list of *any* object. Then the model in question can |
| | 19 | be passed as an extra argument to the URLconf. |
| | 20 | |
| | 21 | Django ships with generic views to do the following: |
| | 22 | |
| | 23 | * Perform common "simple" tasks: redirect to a different page and |
| | 24 | render a given template. |
| | 25 | |
| | 26 | * Display list and detail pages for a single object. If we were creating an |
| | 27 | application to manage conferences then a ``talk_list`` view and a |
| | 28 | ``registered_user_list`` view would be examples of list views. A single |
| | 29 | talk page is an example of what we call a "detail" view. |
| | 30 | |
| | 31 | * Present date-based objects in year/month/day archive pages, |
| | 32 | associated detail, and "latest" pages. The Django Weblog's |
| | 33 | (http://www.djangoproject.com/weblog/) year, month, and |
| | 34 | day archives are built with these, as would be a typical |
| | 35 | newspaper's archives. |
| | 36 | |
| | 37 | * Allow users to create, update, and delete objects -- with or |
| | 38 | without authorization. |
| | 39 | |
| | 40 | Taken together, these views provide easy interfaces to perform the most common |
| | 41 | tasks developers encounter. |
| | 42 | |
| | 43 | Using generic views |
| | 44 | =================== |
| | 45 | |
| | 46 | All of these views are used by creating configuration dictionaries in |
| | 47 | your URLconf files and passing those dictionaries as the third member of the |
| | 48 | URLconf tuple for a given pattern. |
| | 49 | |
| | 50 | For example, here's a simple URLconf you could use to present a static "about" |
| | 51 | page:: |
| | 52 | |
| | 53 | from django.conf.urls.defaults import * |
| | 54 | from django.views.generic.simple import direct_to_template |
| | 55 | |
| | 56 | urlpatterns = patterns('', |
| | 57 | ('^about/$', direct_to_template, { |
| | 58 | 'template': 'about.html' |
| | 59 | }) |
| | 60 | ) |
| | 61 | |
| | 62 | Though this might seem a bit "magical" at first glance -- look, a view with no |
| | 63 | code! --, actually the ``direct_to_template`` view simply grabs information from |
| | 64 | the extra-parameters dictionary and uses that information when rendering the |
| | 65 | view. |
| | 66 | |
| | 67 | Because this generic view -- and all the others -- is a regular view functions |
| | 68 | like any other, we can reuse it inside our own views. As an example, let's |
| | 69 | extend our "about" example to map URLs of the form ``/about/<whatever>/`` to |
| | 70 | statically rendered ``about/<whatever>.html``. We'll do this by first modifying |
| | 71 | the URLconf to point to a view function: |
| | 72 | |
| | 73 | .. parsed-literal:: |
| | 74 | |
| | 75 | from django.conf.urls.defaults import * |
| | 76 | from django.views.generic.simple import direct_to_template |
| | 77 | **from mysite.books.views import about_pages** |
| | 78 | |
| | 79 | urlpatterns = patterns('', |
| | 80 | ('^about/$', direct_to_template, { |
| | 81 | 'template': 'about.html' |
| | 82 | }), |
| | 83 | **('^about/(\w+)/$', about_pages),** |
| | 84 | ) |
| | 85 | |
| | 86 | Next, we'll write the ``about_pages`` view:: |
| | 87 | |
| | 88 | from django.http import Http404 |
| | 89 | from django.template import TemplateDoesNotExist |
| | 90 | from django.views.generic.simple import direct_to_template |
| | 91 | |
| | 92 | def about_pages(request, page): |
| | 93 | try: |
| | 94 | return direct_to_template(request, template="about/%s.html" % page) |
| | 95 | except TemplateDoesNotExist: |
| | 96 | raise Http404() |
| | 97 | |
| | 98 | Here we're treating ``direct_to_template`` like any other function. Since it |
| | 99 | returns an ``HttpResponse``, we can simply return it as-is. The only slightly |
| | 100 | tricky business here is dealing with missing templates. We don't want a |
| | 101 | nonexistent template to cause a server error, so we catch |
| | 102 | ``TemplateDoesNotExist`` exceptions and return 404 errors instead. |
| | 103 | |
| | 104 | .. admonition:: Is there a security vulnerability here? |
| | 105 | |
| | 106 | Sharp-eyed readers may have noticed a possible security hole: we're |
| | 107 | constructing the template name using interpolated content from the browser |
| | 108 | (``template="about/%s.html" % page``). At first glance, this looks like a |
| | 109 | classic *directory traversal* vulnerability. But is it really? |
| | 110 | |
| | 111 | Not exactly. Yes, a maliciously crafted value of ``page`` could cause |
| | 112 | directory traversal, but although ``page`` *is* taken from the request URL, |
| | 113 | not every value will be accepted. The key is in the URLconf: we're using |
| | 114 | the regular expression ``\w+`` to match the ``page`` part of the URL, and |
| | 115 | ``\w`` only accepts letters and numbers. Thus, any malicious characters |
| | 116 | (dots and slashes, here) will be rejected by the URL resolver before they |
| | 117 | reach the view itself. |
| | 118 | |
| | 119 | Generic views of objects |
| | 120 | ======================== |
| | 121 | |
| | 122 | The ``direct_to_template`` certainly is useful, but Django's generic views |
| | 123 | really shine when it comes to presenting views on your database content. Because |
| | 124 | it's such a common task, Django comes with a handful of built-in generic views |
| | 125 | that make generating list and detail views of objects incredibly easy. |
| | 126 | |
| | 127 | Let's take a look at one of these generic views: the "object list" view. We'll |
| | 128 | be using these models:: |
| | 129 | |
| | 130 | # models.py |
| | 131 | from django.db import models |
| | 132 | |
| | 133 | class Publisher(models.Model): |
| | 134 | name = models.CharField(max_length=30) |
| | 135 | address = models.CharField(max_length=50) |
| | 136 | city = models.CharField(max_length=60) |
| | 137 | state_province = models.CharField(max_length=30) |
| | 138 | country = models.CharField(max_length=50) |
| | 139 | website = models.URLField() |
| | 140 | |
| | 141 | def __unicode__(self): |
| | 142 | return self.name |
| | 143 | |
| | 144 | class Meta: |
| | 145 | ordering = ["-name"] |
| | 146 | |
| | 147 | class Book(models.Model): |
| | 148 | title = models.CharField(max_length=100) |
| | 149 | authors = models.ManyToManyField('Author') |
| | 150 | publisher = models.ForeignKey(Publisher) |
| | 151 | publication_date = models.DateField() |
| | 152 | |
| | 153 | To build a list page of all books, we'd use a URLconf along these lines:: |
| | 154 | |
| | 155 | from django.conf.urls.defaults import * |
| | 156 | from django.views.generic import list_detail |
| | 157 | from mysite.books.models import Publisher |
| | 158 | |
| | 159 | publisher_info = { |
| | 160 | "queryset" : Publisher.objects.all(), |
| | 161 | } |
| | 162 | |
| | 163 | urlpatterns = patterns('', |
| | 164 | (r'^publishers/$', list_detail.object_list, publisher_info) |
| | 165 | ) |
| | 166 | |
| | 167 | That's all the Python code we need to write. We still need to write a template, |
| | 168 | however. We could explicitly tell the ``object_list`` view which template to use |
| | 169 | by including a ``template_name`` key in the extra arguments dictionary, but in |
| | 170 | the absence of an explicit template Django will infer one from the object's |
| | 171 | name. In this case, the inferred template will be |
| | 172 | ``"books/publisher_list.html"`` -- the "books" part comes from the name of the |
| | 173 | app that defines the model, while the "publisher" bit is just the lowercased |
| | 174 | version of the model's name. |
| | 175 | |
| | 176 | .. highlightlang:: html+django |
| | 177 | |
| | 178 | This template will be rendered against a context containing a variable called |
| | 179 | ``object_list`` that contains all the book objects. A very simple template |
| | 180 | might look like the following:: |
| | 181 | |
| | 182 | {% extends "base.html" %} |
| | 183 | |
| | 184 | {% block content %} |
| | 185 | <h2>Publishers</h2> |
| | 186 | <ul> |
| | 187 | {% for publisher in object_list %} |
| | 188 | <li>{{ publisher.name }}</li> |
| | 189 | {% endfor %} |
| | 190 | </ul> |
| | 191 | {% endblock %} |
| | 192 | |
| | 193 | That's really all there is to it. All the cool features of generic views come |
| | 194 | from changing the "info" dictionary passed to the generic view. The |
| | 195 | :ref:`generic views reference<ref-generic-views>` documents all the generic |
| | 196 | views and all their options in detail; the rest of this document will consider |
| | 197 | some of the common ways you might customize and extend generic views. |
| | 198 | |
| | 199 | Extending generic views |
| | 200 | ======================= |
| | 201 | |
| | 202 | .. highlightlang:: python |
| | 203 | |
| | 204 | There's no question that using generic views can speed up development |
| | 205 | substantially. In most projects, however, there comes a moment when the |
| | 206 | generic views no longer suffice. Indeed, the most common question asked by new |
| | 207 | Django developers is how to make generic views handle a wider array of |
| | 208 | situations. |
| | 209 | |
| | 210 | Luckily, in nearly every one of these cases, there are ways to simply extend |
| | 211 | generic views to handle a larger array of use cases. These situations usually |
| | 212 | fall into a handful of patterns dealt with in the sections that follow. |
| | 213 | |
| | 214 | Making "friendly" template contexts |
| | 215 | ----------------------------------- |
| | 216 | |
| | 217 | You might have noticed that our sample publisher list template stores all the |
| | 218 | books in a variable named ``object_list``. While this works just fine, it isn't |
| | 219 | all that "friendly" to template authors: they have to "just know" that they're |
| | 220 | dealing with books here. A better name for that variable would be |
| | 221 | ``publisher_list``; that variable's content is pretty obvious. |
| | 222 | |
| | 223 | We can change the name of that variable easily with the ``template_object_name`` |
| | 224 | argument: |
| | 225 | |
| | 226 | .. parsed-literal:: |
| | 227 | |
| | 228 | publisher_info = { |
| | 229 | "queryset" : Publisher.objects.all(), |
| | 230 | **"template_object_name" : "publisher",** |
| | 231 | } |
| | 232 | |
| | 233 | urlpatterns = patterns('', |
| | 234 | (r'^publishers/$', list_detail.object_list, publisher_info) |
| | 235 | ) |
| | 236 | |
| | 237 | Providing a useful ``template_object_name`` is always a good idea. Your |
| | 238 | coworkers who design templates will thank you. |
| | 239 | |
| | 240 | Adding extra context |
| | 241 | -------------------- |
| | 242 | |
| | 243 | Often you simply need to present some extra information beyond that provided by |
| | 244 | the generic view. For example, think of showing a list of all the other |
| | 245 | publishers on each publisher detail page. The ``object_detail`` generic view |
| | 246 | provides the publisher to the context, but it seems there's no way to get a list |
| | 247 | of *all* publishers in that template. |
| | 248 | |
| | 249 | But there is: all generic views take an extra optional parameter, |
| | 250 | ``extra_context``. This is a dictionary of extra objects that will be added to |
| | 251 | the template's context. So, to provide the list of all publishers on the detail |
| | 252 | detail view, we'd use an info dict like this: |
| | 253 | |
| | 254 | .. parsed-literal:: |
| | 255 | |
| | 256 | from mysite.books.models import Publisher, **Book** |
| | 257 | |
| | 258 | publisher_info = { |
| | 259 | "queryset" : Publisher.objects.all(), |
| | 260 | "template_object_name" : "publisher", |
| | 261 | **"extra_context" : {"book_list" : Book.objects.all()}** |
| | 262 | } |
| | 263 | |
| | 264 | This would populate a ``{{ book_list }}`` variable in the template context. |
| | 265 | This pattern can be used to pass any information down into the template for the |
| | 266 | generic view. It's very handy. |
| | 267 | |
| | 268 | However, there's actually a subtle bug here -- can you spot it? |
| | 269 | |
| | 270 | The problem has to do with when the queries in ``extra_context`` are evaluated. |
| | 271 | Because this example puts ``Publisher.objects.all()`` in the URLconf, it will |
| | 272 | be evaluated only once (when the URLconf is first loaded). Once you add or |
| | 273 | remove publishers, you'll notice that the generic view doesn't reflect those |
| | 274 | changes until you reload the Web server (see :ref:`caching-and-querysets` |
| | 275 | for more information about when QuerySets are cached and evaluated). |
| | 276 | |
| | 277 | .. note:: |
| | 278 | |
| | 279 | This problem doesn't apply to the ``queryset`` generic view argument. Since |
| | 280 | Django knows that particular QuerySet should *never* be cached, the generic |
| | 281 | view takes care of clearing the cache when each view is rendered. |
| | 282 | |
| | 283 | The solution is to use a callback in ``extra_context`` instead of a value. Any |
| | 284 | callable (i.e., a function) that's passed to ``extra_context`` will be evaluated |
| | 285 | when the view is rendered (instead of only once). You could do this with an |
| | 286 | explicitly defined function: |
| | 287 | |
| | 288 | .. parsed-literal:: |
| | 289 | |
| | 290 | def get_books(): |
| | 291 | return Book.objects.all() |
| | 292 | |
| | 293 | publisher_info = { |
| | 294 | "queryset" : Publisher.objects.all(), |
| | 295 | "template_object_name" : "publisher", |
| | 296 | "extra_context" : **{"book_list" : get_books}** |
| | 297 | } |
| | 298 | |
| | 299 | or you could use a less obvious but shorter version that relies on the fact that |
| | 300 | ``Publisher.objects.all`` is itself a callable: |
| | 301 | |
| | 302 | .. parsed-literal:: |
| | 303 | |
| | 304 | publisher_info = { |
| | 305 | "queryset" : Publisher.objects.all(), |
| | 306 | "template_object_name" : "publisher", |
| | 307 | "extra_context" : **{"book_list" : Book.objects.all}** |
| | 308 | } |
| | 309 | |
| | 310 | Notice the lack of parentheses after ``Book.objects.all``; this references |
| | 311 | the function without actually calling it (which the generic view will do later). |
| | 312 | |
| | 313 | Viewing subsets of objects |
| | 314 | -------------------------- |
| | 315 | |
| | 316 | Now let's take a closer look at this ``queryset`` key we've been using all |
| | 317 | along. Most generic views take one of these ``queryset`` arguments -- it's how |
| | 318 | the view knows which set of objects to display (see :ref:`topics-db-queries` for |
| | 319 | more information about ``QuerySet`` objects, and see the |
| | 320 | :ref:`generic views reference<ref-generic-views>` for the complete details). |
| | 321 | |
| | 322 | To pick a simple example, we might want to order a list of books by |
| | 323 | publication date, with the most recent first: |
| | 324 | |
| | 325 | .. parsed-literal:: |
| | 326 | |
| | 327 | book_info = { |
| | 328 | "queryset" : Book.objects.all().order_by("-publication_date"), |
| | 329 | } |
| | 330 | |
| | 331 | urlpatterns = patterns('', |
| | 332 | (r'^publishers/$', list_detail.object_list, publisher_info), |
| | 333 | **(r'^books/$', list_detail.object_list, book_info),** |
| | 334 | ) |
| | 335 | |
| | 336 | |
| | 337 | That's a pretty simple example, but it illustrates the idea nicely. Of course, |
| | 338 | you'll usually want to do more than just reorder objects. If you want to |
| | 339 | present a list of books by a particular publisher, you can use the same |
| | 340 | technique: |
| | 341 | |
| | 342 | .. parsed-literal:: |
| | 343 | |
| | 344 | **acme_books = {** |
| | 345 | **"queryset": Book.objects.filter(publisher__name="Acme Publishing"),** |
| | 346 | **"template_name" : "books/acme_list.html"** |
| | 347 | **}** |
| | 348 | |
| | 349 | urlpatterns = patterns('', |
| | 350 | (r'^publishers/$', list_detail.object_list, publisher_info), |
| | 351 | **(r'^books/acme/$', list_detail.object_list, acme_books),** |
| | 352 | ) |
| | 353 | |
| | 354 | Notice that along with a filtered ``queryset``, we're also using a custom |
| | 355 | template name. If we didn't, the generic view would use the same template as the |
| | 356 | "vanilla" object list, which might not be what we want. |
| | 357 | |
| | 358 | Also notice that this isn't a very elegant way of doing publisher-specific |
| | 359 | books. If we want to add another publisher page, we'd need another handful of |
| | 360 | lines in the URLconf, and more than a few publishers would get unreasonable. |
| | 361 | We'll deal with this problem in the next section. |
| | 362 | |
| | 363 | .. note:: |
| | 364 | |
| | 365 | If you get a 404 when requesting ``/books/acme/``, check to ensure you |
| | 366 | actually have a Publisher with the name 'ACME Publishing'. Generic |
| | 367 | views have an ``allow_empty`` parameter for this case. See the |
| | 368 | :ref:`generic views reference<ref-generic-views>` for more details. |
| | 369 | |
| | 370 | Complex filtering with wrapper functions |
| | 371 | ---------------------------------------- |
| | 372 | |
| | 373 | Another common need is to filter down the objects given in a list page by some |
| | 374 | key in the URL. Earlier we hard-coded the publisher's name in the URLconf, but |
| | 375 | what if we wanted to write a view that displayed all the books by some arbitrary |
| | 376 | publisher? We can "wrap" the ``object_list`` generic view to avoid writing a lot |
| | 377 | of code by hand. As usual, we'll start by writing a URLconf: |
| | 378 | |
| | 379 | .. parsed-literal:: |
| | 380 | |
| | 381 | from mysite.books.views import books_by_publisher |
| | 382 | |
| | 383 | urlpatterns = patterns('', |
| | 384 | (r'^publishers/$', list_detail.object_list, publisher_info), |
| | 385 | **(r'^books/(\w+)/$', books_by_publisher),** |
| | 386 | ) |
| | 387 | |
| | 388 | Next, we'll write the ``books_by_publisher`` view itself:: |
| | 389 | |
| | 390 | from django.http import Http404 |
| | 391 | from django.views.generic import list_detail |
| | 392 | from mysite.books.models import Book, Publisher |
| | 393 | |
| | 394 | def books_by_publisher(request, name): |
| | 395 | |
| | 396 | # Look up the publisher (and raise a 404 if it can't be found). |
| | 397 | try: |
| | 398 | publisher = Publisher.objects.get(name__iexact=name) |
| | 399 | except Publisher.DoesNotExist: |
| | 400 | raise Http404 |
| | 401 | |
| | 402 | # Use the object_list view for the heavy lifting. |
| | 403 | return list_detail.object_list( |
| | 404 | request, |
| | 405 | queryset = Book.objects.filter(publisher=publisher), |
| | 406 | template_name = "books/books_by_publisher.html", |
| | 407 | template_object_name = "books", |
| | 408 | extra_context = {"publisher" : publisher} |
| | 409 | ) |
| | 410 | |
| | 411 | This works because there's really nothing special about generic views -- they're |
| | 412 | just Python functions. Like any view function, generic views expect a certain |
| | 413 | set of arguments and return ``HttpResponse`` objects. Thus, it's incredibly easy |
| | 414 | to wrap a small function around a generic view that does additional work before |
| | 415 | (or after; see the next section) handing things off to the generic view. |
| | 416 | |
| | 417 | .. note:: |
| | 418 | |
| | 419 | Notice that in the preceding example we passed the current publisher being |
| | 420 | displayed in the ``extra_context``. This is usually a good idea in wrappers |
| | 421 | of this nature; it lets the template know which "parent" object is currently |
| | 422 | being browsed. |
| | 423 | |
| | 424 | Performing extra work |
| | 425 | --------------------- |
| | 426 | |
| | 427 | The last common pattern we'll look at involves doing some extra work before |
| | 428 | or after calling the generic view. |
| | 429 | |
| | 430 | Imagine we had a ``last_accessed`` field on our ``Author`` object that we were |
| | 431 | using to keep track of the last time anybody looked at that author:: |
| | 432 | |
| | 433 | # models.py |
| | 434 | |
| | 435 | class Author(models.Model): |
| | 436 | salutation = models.CharField(max_length=10) |
| | 437 | first_name = models.CharField(max_length=30) |
| | 438 | last_name = models.CharField(max_length=40) |
| | 439 | email = models.EmailField() |
| | 440 | headshot = models.ImageField(upload_to='/tmp') |
| | 441 | last_accessed = models.DateTimeField() |
| | 442 | |
| | 443 | The generic ``object_detail`` view, of course, wouldn't know anything about this |
| | 444 | field, but once again we could easily write a custom view to keep that field |
| | 445 | updated. |
| | 446 | |
| | 447 | First, we'd need to add an author detail bit in the URLconf to point to a |
| | 448 | custom view: |
| | 449 | |
| | 450 | .. parsed-literal:: |
| | 451 | |
| | 452 | from mysite.books.views import author_detail |
| | 453 | |
| | 454 | urlpatterns = patterns('', |
| | 455 | #... |
| | 456 | **(r'^authors/(?P<author_id>\d+)/$', author_detail),** |
| | 457 | ) |
| | 458 | |
| | 459 | Then we'd write our wrapper function:: |
| | 460 | |
| | 461 | import datetime |
| | 462 | from mysite.books.models import Author |
| | 463 | from django.views.generic import list_detail |
| | 464 | from django.shortcuts import get_object_or_404 |
| | 465 | |
| | 466 | def author_detail(request, author_id): |
| | 467 | # Look up the Author (and raise a 404 if she's not found) |
| | 468 | author = get_object_or_404(Author, pk=author_id) |
| | 469 | |
| | 470 | # Record the last accessed date |
| | 471 | author.last_accessed = datetime.datetime.now() |
| | 472 | author.save() |
| | 473 | |
| | 474 | # Show the detail page |
| | 475 | return list_detail.object_detail( |
| | 476 | request, |
| | 477 | queryset = Author.objects.all(), |
| | 478 | object_id = author_id, |
| | 479 | ) |
| | 480 | |
| | 481 | .. note:: |
| | 482 | |
| | 483 | This code won't actually work unless you create a |
| | 484 | ``books/author_detail.html`` template. |
| | 485 | |
| | 486 | We can use a similar idiom to alter the response returned by the generic view. |
| | 487 | If we wanted to provide a downloadable plain-text version of the list of |
| | 488 | authors, we could use a view like this:: |
| | 489 | |
| | 490 | def author_list_plaintext(request): |
| | 491 | response = list_detail.object_list( |
| | 492 | request, |
| | 493 | queryset = Author.objects.all(), |
| | 494 | mimetype = "text/plain", |
| | 495 | template_name = "books/author_list.txt" |
| | 496 | ) |
| | 497 | response["Content-Disposition"] = "attachment; filename=authors.txt" |
| | 498 | return response |
| | 499 | |
| | 500 | This works because the generic views return simple ``HttpResponse`` objects |
| | 501 | that can be treated like dictionaries to set HTTP headers. This |
| | 502 | ``Content-Disposition`` business, by the way, instructs the browser to |
| | 503 | download and save the page instead of displaying it in the browser. |