The Django Book

You're reading an outdated version of this book; a newer version is available.

Chapter 9: Generic views

Here again is a recurring theme of this book: at its worst, web development is boring and monotonous.

So far we’ve covered how Django tries to take away some of that monotony at the model and template layers, but web developers also experience this boredom at the view level.

Django’s generic views were to developed to ease that pain. They take certain common idioms and patterns in view development and abstract them so that you can quickly write common views of onto data without having to write too much code.

In fact, nearly every view example in the preceding chapters could be re-written with the help of generic views.

Django contains generic views to do the following:

  • Perform common “simple” tasks: redirect to a different page, and render a given template.
  • Display list and detail pages for a single object. For example, the Django documentation index (http://www.djangoproject.com/documentation/) and individual document pages are built this way. The crime index and list of crimes by type views from Chapter 5 could easily be re-written to use generic views; we’ll do so below.
  • Present date-based objects in year/month/day archive pages, associated detail and “latest” pages. The Django weblog’s (http://www.djangoproject.com/weblog/) year, month, and day archives are built with these, as are ljworld.com’s news archives, and a whole host of others.
  • Allow users to create, update, and delete objects — with or without authorization.

Taken together, these views provide easy interfaces to perform the most common tasks developers encounter.

Using generic views

All of these views are used by creating configuration dictionaries in your URLconf files and passing those dictionaries as the third member of the URLconf tuple for a given pattern.

For example, here’s the URLconf for the simple weblog app that drives the blog on djangoproject.com:

from django.conf.urls.defaults import *
from django_website.apps.blog.models import Entry

info_dict = {
    'queryset': Entry.objects.all(),
    'date_field': 'pub_date',
}

urlpatterns = patterns('django.views.generic.date_based',
   (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[-\w]+)/$', 'object_detail', dict(info_dict, slug_field='slug')),
   (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/$',                  'archive_day',   info_dict),
   (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$',                                   'archive_month', info_dict),
   (r'^(?P<year>\d{4})/$',                                                       'archive_year',  info_dict),
   (r'^/?$',                                                                     'archive_index', info_dict),
)

As you can see, this URLconf defines a few options in info_dict. 'queryset' gives the generic view a QuerySet of objects to use (in this case, all of the Entry objects) and tells the generic view which model is being used. The remaining arguments to each generic view are taken from the named captures in the URLconf.

This is really all the “view” code for Django’s weblog! The only thing that’s left is writing a template.

Documentation of each generic view follows, along with a list of all keyword arguments that a generic view expects. Remember that as in the example above, arguments may either come from the URL pattern (as month, day, year, etc. do above) or from the additional-information dictionary (as for queryset, date_field, etc.).

Most generic views require the queryset key, which is a QuerySet instance; see the database API reference in Appendix 3 for more information about QuerySet objects.

Most views also take an optional extra_context dictionary that you can use to pass any auxiliary information you wish to the view. The values in the extra_context dictionary can be either functions (or other callables) or other objects. Functions are evaluated just before they are passed to the template.

“Simple” generic views

The django.views.generic.simple module contains simple views to handle a couple of common cases: rendering a template when no view logic is needed, and issuing a redirect.

Rendering a template

The function django.views.generic.simple.direct_to_template renders a given template, passing it a {{ params }} template variable, which is a dictionary of the parameters captured in the URL.

Example

Given the following URL patterns:

urlpatterns = patterns('django.views.generic.simple',
    (r'^foo/$',             'direct_to_template', {'template': 'foo_index.html'}),
    (r'^foo/(?P<id>\d+)/$', 'direct_to_template', {'template': 'foo_detail.html'}),
)

a request to /foo/ would render the template foo_index.html, and a request to /foo/15/ would render the foo_detail.html with a context variable {{ params.id }} that is set to 15.

Required arguments
template
The full name of a template to use.

Redirecting to another URL

django.views.generic.simple.redirect_to redirects to another URL. The given URL may contain dictionary-style string formatting, which will be interpolated against the parameters captured in the URL.

If the given URL is None, Django will return an HTTP 410 (Gone) message.

Example

This example redirects from /foo/<id>/ to /bar/<id>/:

urlpatterns = patterns('django.views.generic.simple',
    ('^foo/(?p<id>\d+)/$', 'redirect_to', {'url': '/bar/%(id)s/'}),
)

This example returns a 410 HTTP error for requests to /bar/:

urlpatterns = patterns('django.views.generic.simple',
    ('^bar/$', 'redirect_to', {'url': None}),
)
Required arguments
url
The URL to redirect to, as a string. Or None to return a 410 (“gone”) HTTP response.

More complex generic views

Although the simple generic views certainly are useful, the real power in Django’s generic views comes from the more complex views that allow you to build common CRUD (Create/Retrieve/Update/Delete) pages with a minimum amount of code.

These views break down into a few different types:

  • List/detail views, which provide flat lists of objects and individual object detail pages (for example, a list of places and individual place information pages).
  • Date-based views, which provide year/month/day drill-down pages of date-centric information.
  • Create/update/delete views, which allow you to quickly create views to create, modify, or delete objects.

Common optional arguments

Most of these views take a large number of optional arguments that can control various bits of behavior. Many of these arguments may be given to any of these views, so many of the views below refer back to this list of optional arguments:

allow_empty
A boolean specifying whether to display the page if no objects are available. If this is False and no objects are available, the view will raise a 404 instead of displaying an empty page. By default, this is False.
context_processors
A list of template-context processors to apply to the view’s template. See Chapter 10 for information on template context processors.
extra_context
A dictionary of values to add to the template context. By default, this is an empty dictionary. If a value in the dictionary is callable, the generic view will call it just before rendering the template.
mimetype
The MIME type to use for the resulting document. Defaults to the value of the DEFAULT_MIME_TYPE setting.
template_loader
The template loader to use when loading the template. By default, it’s django.template.loader. See Chapter 10 for information on template loaders.
template_name
The full name of a template to use in rendering the page. This lets you override the default template name derived from the QuerySet.
template_object_name
Designates the name of the template variable to use in the template context. By default, this is 'object'. Views list list more than one objec will append '_list' to the value of this parameter.

List/detail generic views

The list-detail generic views (in the module django.views.generic.list_detail) handles the common case of displaying a list of items at one view, and individual “detail” views of those items at another.

For the examples in the rest of this chapter, we’ll be working with the simple book/author/publisher objects from chapters 5 and 6:

class Publisher(models.Model):
    name = models.CharField(maxlength=30)
    address = models.CharField(maxlength=50)
    city = models.CharField(maxlength=60)
    state_province = models.CharField(maxlength=30)
    country = models.CharField(maxlength=50)
    website = models.URLField()

class Author(models.Model):
    salutation = models.CharField(maxlength=10)
    first_name = models.CharField(maxlength=30)
    last_name = models.CharField(maxlength=40)
    email = models.EmailField()
    headshot = models.ImageField()

class Book(models.ModelField):
    title = models.CharField(maxlength=100)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    publication_date = models.DateField()

We’ll also be working with a URL module; if you’re following along, you can start with an skeleton URL config in bookstore.urls:

from django.conf.urls.defaults import *
from django.views.generic import list_detail, date_based, create_update
from bookstore.models import Publisher, Author, Book

urlpatterns = patterns('',
    # We'll add URL patterns here.
)

We’ll build this up with generic views as we go.

Lists of objects

The view django.views.generic.list_detail.object_list is used to create a page representing a list of objects.

Example

We can use the object_list view to show a simple list of all authors in the bookstore. First, we’ll need to construct a info dictionary for the generic view. Add the following to the top of the bookstore/urls.py file:

author_list_info = {
    'queryset' :   Author.objects.all(),
    'allow_empty': True,
}

Then, we need to register this view at a certain URL. We can do that by adding this URL config piece (inside the patterns directive):

(r'authors/$', list_detail.object_list, author_list_info)

From there, we just need to make a template for this generic view to render. Since we didn’t provide the template_name parameter (see below), Django will guess the name of the template; here it’ll use bookstore/author_list.html. See below for more details on how this “guess” is made.

Required arguments
queryset
A QuerySet of objects to list
Optional arguments
paginate_by
An integer specifying how many objects should be displayed per page. If this is given, the view will paginate objects with paginate_by objects per page. The view will expect either a page query string parameter (via GET) containing a zero-indexed page number, or a page variable specified in the URLconf. See “Notes on pagination” below.

Additionally, this view may take any of these common arguments described above:

  • allow_empty
  • context_processors
  • extra_context
  • mimetype
  • template_loader
  • template_name
  • template_object_name
Template name

If template_name isn’t specified, this view will use the template <app_label>/<model_name>_list.html by default. Both the app label and the model name are derived from the queryset parameter: the app label is the name of the app that the model is defined in, and the model name is the lower-cased version of the name of the model class.

So, if we passed Author.objects.all() as the queryset, the app label would be bookstore and the model name would be author. This means the default template would be bookstore/author_list.html.

Template context

In addition to extra_context, the template’s context will contain:

object_list
The list of objects. This variable’s name depends on the template_object_name parameter, which is 'object' by default. If template_object_name is 'foo', this variable’s name will be foo_list.
is_paginated
A boolean representing whether the results are paginated. Specifically, this is set to False if the number of available objects is less than or equal to paginate_by.

If the results are paginated, the context will contain these extra variables:

results_per_page
The number of objects per page. (Same as the paginate_by parameter.)
has_next
A boolean representing whether there’s a next page.
has_previous
A boolean representing whether there’s a previous page.
page
The current page number, as an integer. This is 1-based.
next
The next page number, as an integer. If there’s no next page, this will still be an integer representing the theoretical next-page number. This is 1-based.
previous
The previous page number, as an integer. This is 1-based.
pages
The total number of pages, as an integer.
hits
The total number of objects across all pages, not just this page.

A note on pagination:

If paginate_by is specified, Django will paginate the results. You can specify the page number in the URL in one of two ways:

  • Use the page parameter in the URLconf. For example, this is what your URLconf might look like:

    (r'^objects/page(?P<page>[0-9]+)/$', 'object_list', dict(info_dict))
    
  • Pass the page number via the page query-string parameter. For example, a URL would look like this:

    /objects/?page=3

In both cases, page is 1-based, not 0-based, so the first page would be represented as page 1.

Detail views

The django.views.generic.list_detail.object_detail gives a “detail” view of a single object.

Example

Extending the example above, we could make a detail view for a given author. Given an info dict like this:

author_detail_info = {
    "queryset" : Author.objects.all(),
    "template_object_name" : "author",
}

We could use a urlpattern like:

(r'^authors/(?P<object_id>\d+)/$', list_detail.object_detail, author_detail_info),

to show details about a given book, rendered in the bookstore/author_detail.html template. In that template, the Author object itself would be put into the {{ author }} variable.

Required arguments
queryset`
A QuerySet that will be searched for the object.

Either:

object_id
The value of the primary-key field for the object.

or:

slug
The slug of the given object. If you pass this field, then the slug_field argument (below) is also required.
Optional arguments
slug_field
The name of the field on the object containing the slug. This is required if you are using the slug argument, but must be absent if you’re using the object_id argument.
template_name_field

The name of a field on the object whose value is the template name to use. This lets you store template names in your data.

In other words, if your object has a field 'the_template' that contains a string 'foo.html', and you set template_name_field to 'the_template', then the generic view for this object will use the template 'foo.html'.

It’s a bit of a brain-bender, but it’s useful in some cases.

This view may also take these common arguments (documented above):

  • context_processors
  • extra_context
  • mimetype
  • template_loader
  • template_name
  • template_object_name
Template name

If template_name and template_name_field aren’t specified, this view will use the template <app_label>/<model_name>_detail.html by default.

Template context

In addition to extra_context, the template’s context will be:

object
The object. This variable’s name depends on the template_object_name parameter, which is 'object' by default. If template_object_name is 'foo', this variable’s name will be foo.

Date-based generic views

Date-based generic views are generally used to provide a set of “archive” pages for dated material. Think year/month/day archives for a newspaper, or a blog like the official Django blog described at the beginning of this chapter.

For the examples, we’ll be using the Book object from above, and build up a way to browse books by year, month, and day published. Notice that for each of these views, we have to tell Django the name of the date field we want to key off of. We have to provide this information since models could contain multiple date or datetime fields.

Into the future…

By default, these views ignore objects with dates in the future.

This means that if you try to visit an archive page in the future, Django will automatically show a 404 (“not found”) error, even if there are objects published that day.

Thus, you can publish post-dated objects that don’t appear publically until after their publication date.

However, for different types of date-based objects this isn’t appropriate (for example, a calendar of upcoming events). For these views, setting the allow_future option to True will make the future objects appear (and allow users to visit “future” archive pages).

Archive index

The django.views.generic.date_based.archive_index view provides a top-level index page showing the “latest” objects, by date.

Example

A typical publisher probably wants to highlight recently-published books. We can use the archive_index view for this common task. Here’s a info dict:

book_info = {
    "queryset"   : Book.objects.all(),
    "date_field" : "publication_date"
}

And the corresponding urlconf piece (which roots this index at the bottom level of wherever it’s included):

(r'^books/$', date_based.archive_index, book_info),
Required arguments
date_field:
The name of the DateField or DateTimeField in the QuerySet‘s model that the date-based archive should use to determine the objects on the page.
queryset
A QuerySet of objects for which the archive serves.
Optional arguments
allow_future
A boolean specifying whether to include “future” objects on this page, as described in the note above.
num_latest
The number of latest objects to send to the template context. By default, it’s 15.

This view may also take these common arguments (documented above):

  • allow_empty
  • context_processors
  • extra_context
  • mimetype
  • template_loader
  • template_name
Template name

If template_name isn’t specified, this view will use the template <app_label>/<model_name>_archive.html by default.

Template context

In addition to extra_context, the template’s context will be:

date_list

A list of datetime.date objects representing all years that have objects available according to queryset. These are ordered in reverse.

For example, if you have blog entries from 2003 through 2006, this list will contain four datetime.date objects: one for each of those years.

latest
The num_latest objects in the system, ordered descending by date_field. For example, if num_latest is 10, then latest will be a list of the latest 10 objects in queryset.

Year archives

The django.views.generic.date_based.archive_year view provides a yearly archive page showing all available months in a given year.

Example

Contuting on with our example, we’ll want to add a way to view all the books published in a given year. We can keep using the book_info dictionary from the above example, but this time we’ll wire it up to the archive_year view:

(r'^books/(?P<year>\d{4})/?$', date_based.archive_year, book_info),

Since there are likely many, many books published each year, we won’t display them on this page, just a list of years in which books are available. Conveniently for us, this is what Django does by default; to change it we could use the make_object_list argument; see below.

Required arguments
date_field
As above.
queryset
A QuerySet of objects for which the archive serves.
year
The four-digit year for which the archive serves (usually taken from URL parameters).
Optional arguments
make_object_list
A boolean specifying whether to retrieve the full list of objects for this year and pass those to the template. If True, this list of objects will be made available to the template as object_list. (The name object_list may be different; see the information about object_list in the “Template context” section below.) By default, this is False.
allow_future
A boolean specifying whether to include “future” objects on this page, as described in the note above.

This view may also take these common arguments (documented above):

  • allow_empty
  • context_processors
  • extra_context
  • mimetype
  • template_loader
  • template_name
  • template_object_name
Template name

If template_name isn’t specified, this view will use the template <app_label>/<model_name>_archive_year.html by default.

Template context

In addition to extra_context, the template’s context will be:

date_list
A list of datetime.date objects representing all months that have objects available in the given year, according to queryset, in ascending order.
year
The given year, as a four-character string.
object_list

If the make_object_list parameter is True, this will be set to a list of objects available for the given year, ordered by the date field. This variable’s name depends on the template_object_name parameter, which is 'object' by default. If template_object_name is 'foo', this variable’s name will be foo_list.

If make_object_list is False, object_list will be passed to the template as an empty list.

Monthly archives

The django.views.generic.date_based.archive_month views provides a monthly archive page showing all objects in a given month.

Example

Continuing on with our example, creating month views should look mighty familiar:

(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', date_based.archive_month, book_info),
Required arguments
year
The four-digit year for which the archive serves (a string).
month
The month for which the archive serves, formatted according to the month_format argument.
queryset
A QuerySet of objects for which the archive serves.
date_field
The name of the DateField or DateTimeField in the QuerySet‘s model that the date-based archive should use to determine the objects on the page.
Optional arguments
month_format
A format string that regulates what format the month parameter uses. This should be in the syntax accepted by Python’s time.strftime. (See Python’s strftime docs at http://www.python.org/doc/current/lib/module-time.html#l2h-1941) It’s set to "%b" by default, which is a three-letter month abbreviation (i.e. “jan”, “feb”, etc.). To change it to use numbers, use "%m".
allow_future
A boolean specifying whether to include “future” objects on this page, as described in the note above.

This view may also take these common arguments (documented above):

  • allow_empty
  • context_processors
  • extra_context
  • mimetype
  • template_loader
  • template_name
  • template_object_name
Template name

If template_name isn’t specified, this view will use the template <app_label>/<model_name>_archive_month.html by default.

Template context

In addition to extra_context, the template’s context will be:

month
A datetime.date object representing the given month.
next_month
A datetime.date object representing the first day of the next month. If the next month is in the future, this will be None.
previous_month
A datetime.date object representing the first day of the previous month. Unlike next_month, this will never be None.
object_list
A list of objects available for the given month. This variable’s name depends on the template_object_name parameter, which is 'object' by default. If template_object_name is 'foo', this variable’s name will be foo_list.

Week archives

The django.views.generic.date_based.archive_week view shows all objects in a given week.

Note

Django believes that weeks start on Sunday, for the perfectly arbitrary reason that Python does, too.

Example

Are you starting to see a pattern here yet?

(r'^(?P<year>\d{4})/(?P<week>\d{2})/$', date_based.archive_week, book_info),
Required arguments
year
The four-digit year for which the archive serves (a string).
week
The week of the year for which the archive serves (a string).
queryset
A QuerySet of objects for which the archive serves.
date_field
The name of the DateField or DateTimeField in the QuerySet‘s model that the date-based archive should use to determine the objects on the page.

Optional arguments

allow_future
A boolean specifying whether to include “future” objects on this page, as described in the note above.

This view may also take these common arguments (documented above):

  • allow_empty
  • context_processors
  • extra_context
  • mimetype
  • template_loader
  • template_name
  • template_object_name
Template name

If template_name isn’t specified, this view will use the template <app_label>/<model_name>_archive_week.html by default.

Template context

In addition to extra_context, the template’s context will be:

week
A datetime.date object representing the first day of the given week.
object_list
A list of objects available for the given week. This variable’s name depends on the template_object_name parameter, which is 'object' by default. If template_object_name is 'foo', this variable’s name will be foo_list.

Day archives

The django.views.generic.date_based.archive_day view provides a page showing all objects in a given day.

Example

Keep on keepin’ on:

(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{2})/$', date_based.archive_day, book_info),
Required arguments
year
The four-digit year for which the archive serves (a string).
month
The month for which the archive serves, formatted according to the month_format argument.
day
The day for which the archive serves, formatted according to the day_format argument.
queryset
A QuerySet of objects for which the archive serves.
date_field
The name of the DateField or DateTimeField in the QuerySet‘s model that the date-based archive should use to determine the objects on the page.
Optional arguments
month_format
A format string that regulates what format the month parameter uses. See the detailed explanation above.
day_format
Like month_format, but for the day parameter. It defaults to "%d" (day of the month as a decimal number, 01-31).
allow_future
A boolean specifying whether to include “future” objects on this page, as described in the note above.

This view may also take these common arguments (documented above):

  • allow_empty
  • context_processors
  • extra_context
  • mimetype
  • template_loader
  • template_name
  • template_object_name
Template name

If template_name isn’t specified, this view will use the template <app_label>/<model_name>_archive_day.html by default.

Template context

In addition to extra_context, the template’s context will be:

day
A datetime.date object representing the given day.
next_day
A datetime.date object representing the next day. If the next day is in the future, this will be None.
previous_day
A datetime.date object representing the given day. Unlike next_day, this will never be None.
object_list
A list of objects available for the given day. This variable’s name depends on the template_object_name parameter, which is 'object' by default. If template_object_name is 'foo', this variable’s name will be foo_list.

Archive for today

The django.views.generic.date_based.archive_today view shows all objects for today. This is exactly the same as archive_day, except the year/month/day arguments are not used, and today’s date is used instead.

Date-based detail pages

The django.views.generic.date_based.object_detail view shows a page representing an individual object. This differs from the object_detail page in their respective URLs; the object_detail view uses URLs like /entries/<slug>/, while this one uses URLs like /entries/2006/aug/27/<slug>/.

Note

If you’re using date-based detail pages with slugs in the URLs, you probably also want to use the unique_for_date option on the slug field to validate that slugs aren’t duplicated in a single day. See Appendix 2 for details on unique_for_date.

Example

This one differs (slightly) from all the other examples in that we need to either provide an object ID or a slug so that Django can look up the object in question.

Since the object we’re using doesn’t have a slug field, we’ll use the slightly uglyier ID-based URLs. In practice we’d prefer to use a slug field, but in the interest of simplicity we’ll let it go.

We’ll add the following to the URLconf:

(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{2})/(?P<object_id>[\w-]+)/$', date_based.object_detail, book_info),
Required arguments
year
The object’s four-digit year (a string).
month
The object’s month , formatted according to the month_format argument.
day
The object’s day , formatted according to the day_format argument.
queryset
A QuerySet that contains the object.
date_field
The name of the DateField or DateTimeField in the QuerySet‘s model that the generic view should use to look up the object according to year, month and day.

Either:

object_id
The value of the primary-key field for the object.

or:

slug
The slug of the given object. If you pass this field, then the slug_field argument (below) is also required.
Optional arguments
allow_future
A boolean specifying whether to include “future” objects on this page, as described in the note above.
day_format
Like month_format, but for the day parameter. It defaults to "%d" (day of the month as a decimal number, 01-31).
month_format
A format string that regulates what format the month parameter uses. See the detailed explanation above.
slug_field
The name of the field on the object containing the slug. This is required if you are using the slug argument, but must be absent if you’re using the object_id argument.
template_name_field

The name of a field on the object whose value is the template name to use. This lets you store template names in the data. In other words, if your object has a field 'the_template' that contains a string 'foo.html', and you set template_name_field to 'the_template', then the generic view for this object will use the template 'foo.html'.

It’s a bit of a brain-bender, but it’s useful in some cases.

This view may also take these common arguments (documented above):

  • context_processors
  • extra_context
  • mimetype
  • template_loader
  • template_name
  • template_object_name
Template name

If template_name isn’t specified, this view will use the template <app_label>/<model_name>_detail.html by default.

Template context

In addition to extra_context, the template’s context will be:

object
The object. This variable’s name depends on the template_object_name parameter, which is 'object' by default. If template_object_name is 'foo', this variable’s name will be foo.

Create/update/delete generic views

Note

These views will change slightly when Django’s revised form architecture (currently under development as django.newforms) is finalized. This section will be updated accordingly.

The django.views.generic.create_update module contains a set of functions for creating, editing and deleting objects.

Create object view

The django.views.generic.create_update.create_object view displays a form for creating an object, redisplays the form with validation errors (if there are any) and saves the object. This uses the automatic manipulators that come with Django models.

These views all present forms if accessed with a GET and perform the requested action (create/update/delete) if accessed via POST.

Note that these views all have a very rough idea of security. Although they take a login_required attribute which if given will restrict access to logged-in users, that’s as far as it goes. They won’t, for example, check that the user editing an object is the same user that created it, nor will they validate any sort of permissions.

Much of the time, however, those features can be accomplished by writing a small wrapper around the generic view; see “extending generic views”, below, for more about this topic.

Example

If we wanted to allow users to create new books in our database, we could do something like this:

(r'^books/create/$', create_update.create_object, {'model' : Book}),
Required arguments
model
The Django model of the object that the form will create.

Note

Notice that this view takes the model to be created, not a QuerySet (as all the list/detail/date-based views above do).

Optional arguments
post_save_redirect
A URL to which the view will redirect after saving the object. By default, it’s object.get_absolute_url().
post_save_redirect
May contain dictionary string formatting, which will be interpolated against the object’s field attributes. For example, you could use post_save_redirect="/polls/%(slug)s/".
login_required

A boolean that designates whether a user must be logged in, in order to see the page and save changes. This hooks into the Django authentication system. By default, this is False.

If this is True, and a non-logged-in user attempts to visit this page or save the form, Django will redirect the request to /accounts/login/.

This view may also take these common arguments (documented above):

  • context_processors
  • extra_context
  • template_loader
  • template_name
Template name

If template_name isn’t specified, this view will use the template <app_label>/<model_name>_form.html by default.

Template context

In addition to extra_context, the template’s context will be:

form:

A FormWrapper instance representing the form for editing the object. This lets you refer to form fields easily in the template system.

For example, if the model has two fields, name and address:

<form action="" method="post">
<p><label for="id_name">Name:</label> {{ form.name }}</p>
<p><label for="id_address">Address:</label> {{ form.address }}</p>
</form>

See Chapter 7 for more information about working with forms.

Update object view

The django.views.generic.create_update.update_object view is almost identical to the create-object view above, but this one allows the editing of an existing object instead of the creation of a new one.

Example

Following the above example, we could provide an edit interface for a single book with this URLconf snippet:

(r'^books/edit/(?P<object_id>\d+)/$', create_update.update_object, {'model' : Book}),
Required arguments
model
The Django model the form will be editing.

Either:

object_id
The value of the primary-key field for the object.

or:

slug
The slug of the given object. If you pass this field, then the slug_field argument (below) is also required.
Optional arguments
slug_field
The name of the field on the object containing the slug. This is required if you are using the slug argument, but must be absent if you’re using the object_id argument.

Additionally, this view takes all same optional arguments as the creation view (above), plus the template_object_name common argument.

Template name

This view uses the same default template name (<app_label>/<model_name>_form.html) as the creation view.

Template context

In addition to extra_context, the template’s context will be:

form:
A FormWrapper instance representing the form for editing the object. See the create object (above) for more about this value.
object:
The original object being edited (this variable may be named differently if you’ve provided the template_object_name argument).

Delete object view

The django.views.generic.create_update.delete_object view is also very similar to the other two.

If this view is fetched with GET, it will display a confirmation page (i.e. “do you really want to delete this object?”). If the view is submitted with POST, the object will be deleted without confirmation.

All the arguments are the same as for the update object view, as is the context; the template name for this view is <app_label>/<model_name>_confirm_delete.html

Extending generic views

There’s no question that using generic views can speed up development substantially. In most projects, however, there comes a moment when the generic views no longer suffice. Indeed, the most common question asked by new Django developers is about how to make generic views handle a wider array of situations.

Luckily, in nearly every one of these cases, there are ways to simply extend generic views to handle a larger array of use cases. These situations usually fall into a couple of patterns:

Adding extra context

Often you simply need to present some extra information than that provided by the generic view. For example, think of showing a list of all publishers on a book’s detail page; the object_detail generic view provides the book to the context, but it seems there’s no way to get a list of publishers in that template.

But there is: all generic views take an extra optional parameter extra_context. This is a dictionary of extra objects which will be added to the template’s context. So, to provide the list of publishers in the book detail view, we’d use an info dict like this:

book_info = {
    "queryset"   : Book.objects.all(),
    "date_field" : "publication_date",
    "extra_context" : {
        "publisher_list" : Publisher.objects.all(),
    }
}

This would populate a {{ publisher_list }} variable in the template context. This pattern can be used to pass any information down into the template for the generic view; it’s very handy.

More complex filtering with wrapper functions

Another common need is to filter down the objects given in a list page by some key in the URL. For example, let’s look at providing an interface to browse books by title. We’d like to provide URLs of the form /books/by-title/a/, /books/by-title/b/, etc. — one list page for each letter of the alphabet.

The problem seems to be that the generic view has no concept of reading variables from the URL; if we wired a URL pattern matching those URLs up to the object_list view, we’d get twenty-six pages displaying all the books. Although we could write twenty-six different info dicts (each with a different queryset argument), that’s just silly. The right technique involves writing a simple “wrapper” function around the generic view.

In our alphabetic-browsing example, we’d start by adding a small bit to the URLconf:

from bookstore.views import browse_alphabetically

urlpatterns = patterns('',
    # ...
    (r'^books/by-title/([a-z])/$', browse_alphabetically)
)

As you can see, this wires the set of URLs to the browse_alphabetically function, so let’s take a look at how that function could be written:

from bookstore.models. import Book
from django.views.generic import list_detail

def browse_alphabetically(request, letter):
    return list_detail.object_list(
        request,
        queryset = Book.objects.filter(title__istartswith=letter),
        template_name = "bookstore/browse_alphabetically.html",
        extra_context = {
            'letter' : letter,
        }
    )

That’s it!

This works because there’s really nothing special about generic views — they’re just Python functions. Like any view function, generic views expect a certain set of arguments and return HttpResponse objects. Thus, it’s incredibly easy to wrap a small function around a generic view that does additional work before — or after; see below — handing things off to the generic view.

Note

Notice that in the above example we’ve passed the current letter being display in the extra_context. This is usually a good idea in wrappers of this nature; it lets the template know which letter is currently being browsed.

Also (while we’re on the topic of templates) notice that we’ve passed in a custom template name. Without that, it would try to use the same template as a “vanilla” object_list, which could conflict with other generic views.

Performing extra work

The last common pattern we’ll look at involves doing some extra work before or after calling the generic view.

Imagine we had a last_accessed field on our Author object that we were using to keep track of the last time a anybody looked at that author. The generic object_detail view, of course, wouldn’t know anything about this field, but once again we could easily write a custom view to keep that field updated.

First, we’d need to modify the author detail bit in the URLconf to point to a custom view:

from bookstore.views import author_detail

urlpatterns = patterns('',
    #...
    (r'^authors/(?P<author_id>d+)/$', author_detail),
)

Then we’d write our wrapper function:

import datetime
from bookstore.models import Author
from django.views.generic import list_detail
from django.shortcuts import get_object_or_404

def author_detail(request, author_id):
    # Look up the Author (and raise a 404 if she's not found)
    author = get_object_or_404(Author, pk=author_id)

    # Record the last accessed date
    author.last_accessed = datetime.datetime.now()
    author.save()

    # Show the detail page
    return list_detail.object_detail(
        request,
        queryset = Author.objects.all(),
        object_id = author_id,
    )

Note

This code won’t actually work unless you add the last_accessed field to your Author model.

We can use a similar idiom to alter the response returned by the generic view. If we wanted to provide a downloadable plain-text version of the list of authors, we could use a view like this:

def author_list_plaintext(request):
    response = list_detail.object_list(
        queryset = Author.objects.all(),
        mimetype = "text/plain",
        template_name = "bookstore/author_list.txt"
    )
    response["Content-Disposition"] = "attachment; filename=authors.txt"
    return response

This works because the generic views return simple HttpResponse objects which can be treated like dictionaries to set HTTP headers. This Content-Disposition business, by the way, instructs the browser to download and save the page instead of displaying it in the browser.

What’s next?

Until now, we’ve treated the template engine as a mostly static tool you can use to render your content. It’s true that most of the time you’ll just treat it in that way, but the template engine is actually quite extensible.

In the next chapter we’ll delve deep into the inner workings of Django’s templates, showing all the cool ways it can be extended.

Onward, comrades!

Copyright 2006 Adrian Holovaty and Jacob Kaplan-Moss.
This work is licensed under the GNU Free Document License.
Hosting graciously provided by media temple
Comments X

Comments are closed on this chapter.

We're no longer accepting comments on this version of this chapter.

Many thanks to all those who commented.

      About this comment system

      This site is using a contextual comment system to help us gather targeted feedback about the book. Instead of commenting on an entire chapter, you can leave comments on any indivdual "block" in the chapter. A "block" with comments looks like this:

      A "block" is a paragraph, list item, code sample, or other small chunk of content. It'll get highlighted when you select it:

      To post a comment on a block, just click in the gutter next to the bit you want to comment on:

      As we edit the book, we'll review everyone's comments and roll them into a future version of the book. We'll mark reviewed comments with a little checkmark:

      Please make sure to leave a full name (and not a nickname or screenname) if you'd like your contributions acknowledged in print.

      Many, many thanks to Jack Slocum; the inspiration and much of the code for the comment system comes from Jack's blog, and this site couldn't have been built without his wonderful YAHOO.ext library. Thanks also to Yahoo for YUI itself.