1
.. _topics-generic-views:
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
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
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.
21
Django ships with generic views to do the following:
23
* Perform common "simple" tasks: redirect to a different page and
24
render a given template.
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.
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
37
* Allow users to create, update, and delete objects -- with or
38
without authorization.
40
Taken together, these views provide easy interfaces to perform the most common
41
tasks developers encounter.
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.
50
For example, here's a simple URLconf you could use to present a static "about"
53
from django.conf.urls.defaults import *
54
from django.views.generic.simple import direct_to_template
56
urlpatterns = patterns('',
57
('^about/$', direct_to_template, {
58
'template': 'about.html'
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
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:
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**
79
urlpatterns = patterns('',
80
('^about/$', direct_to_template, {
81
'template': 'about.html'
83
**('^about/(\w+)/$', about_pages),**
86
Next, we'll write the ``about_pages`` view::
88
from django.http import Http404
89
from django.template import TemplateDoesNotExist
90
from django.views.generic.simple import direct_to_template
92
def about_pages(request, page):
94
return direct_to_template(request, template="about/%s.html" % page)
95
except TemplateDoesNotExist:
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.
104
.. admonition:: Is there a security vulnerability here?
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?
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.
119
Generic views of objects
120
========================
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.
127
Let's take a look at one of these generic views: the "object list" view. We'll
128
be using these models::
131
from django.db import models
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()
141
def __unicode__(self):
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()
153
To build a list page of all books, we'd use a URLconf along these lines::
155
from django.conf.urls.defaults import *
156
from django.views.generic import list_detail
157
from mysite.books.models import Publisher
160
"queryset" : Publisher.objects.all(),
163
urlpatterns = patterns('',
164
(r'^publishers/$', list_detail.object_list, publisher_info)
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.
176
.. highlightlang:: html+django
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::
182
{% extends "base.html" %}
187
{% for publisher in object_list %}
188
<li>{{ publisher.name }}</li>
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.
199
Extending generic views
200
=======================
202
.. highlightlang:: python
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
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.
214
Making "friendly" template contexts
215
-----------------------------------
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.
223
We can change the name of that variable easily with the ``template_object_name``
229
"queryset" : Publisher.objects.all(),
230
**"template_object_name" : "publisher",**
233
urlpatterns = patterns('',
234
(r'^publishers/$', list_detail.object_list, publisher_info)
237
Providing a useful ``template_object_name`` is always a good idea. Your
238
coworkers who design templates will thank you.
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.
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:
256
from mysite.books.models import Publisher, **Book**
259
"queryset" : Publisher.objects.all(),
260
"template_object_name" : "publisher",
261
**"extra_context" : {"book_list" : Book.objects.all()}**
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.
268
However, there's actually a subtle bug here -- can you spot it?
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).
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.
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:
291
return Book.objects.all()
294
"queryset" : Publisher.objects.all(),
295
"template_object_name" : "publisher",
296
"extra_context" : **{"book_list" : get_books}**
299
or you could use a less obvious but shorter version that relies on the fact that
300
``Book.objects.all`` is itself a callable:
305
"queryset" : Publisher.objects.all(),
306
"template_object_name" : "publisher",
307
"extra_context" : **{"book_list" : Book.objects.all}**
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).
313
Viewing subsets of objects
314
--------------------------
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).
322
To pick a simple example, we might want to order a list of books by
323
publication date, with the most recent first:
328
"queryset" : Book.objects.all().order_by("-publication_date"),
331
urlpatterns = patterns('',
332
(r'^publishers/$', list_detail.object_list, publisher_info),
333
**(r'^books/$', list_detail.object_list, book_info),**
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
345
**"queryset": Book.objects.filter(publisher__name="Acme Publishing"),**
346
**"template_name" : "books/acme_list.html"**
349
urlpatterns = patterns('',
350
(r'^publishers/$', list_detail.object_list, publisher_info),
351
**(r'^books/acme/$', list_detail.object_list, acme_books),**
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.
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.
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.
370
Complex filtering with wrapper functions
371
----------------------------------------
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:
381
from mysite.books.views import books_by_publisher
383
urlpatterns = patterns('',
384
(r'^publishers/$', list_detail.object_list, publisher_info),
385
**(r'^books/(\w+)/$', books_by_publisher),**
388
Next, we'll write the ``books_by_publisher`` view itself::
390
from django.http import Http404
391
from django.views.generic import list_detail
392
from mysite.books.models import Book, Publisher
394
def books_by_publisher(request, name):
396
# Look up the publisher (and raise a 404 if it can't be found).
398
publisher = Publisher.objects.get(name__iexact=name)
399
except Publisher.DoesNotExist:
402
# Use the object_list view for the heavy lifting.
403
return list_detail.object_list(
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}
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.
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
424
Performing extra work
425
---------------------
427
The last common pattern we'll look at involves doing some extra work before
428
or after calling the generic view.
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::
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()
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
447
First, we'd need to add an author detail bit in the URLconf to point to a
452
from mysite.books.views import author_detail
454
urlpatterns = patterns('',
456
**(r'^authors/(?P<author_id>\d+)/$', author_detail),**
459
Then we'd write our wrapper function::
462
from mysite.books.models import Author
463
from django.views.generic import list_detail
464
from django.shortcuts import get_object_or_404
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)
470
# Record the last accessed date
471
author.last_accessed = datetime.datetime.now()
474
# Show the detail page
475
return list_detail.object_detail(
477
queryset = Author.objects.all(),
478
object_id = author_id,
483
This code won't actually work unless you create a
484
``books/author_detail.html`` template.
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::
490
def author_list_plaintext(request):
491
response = list_detail.object_list(
493
queryset = Author.objects.all(),
494
mimetype = "text/plain",
495
template_name = "books/author_list.txt"
497
response["Content-Disposition"] = "attachment; filename=authors.txt"
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.