---
title: Class-Based Generic Views in Django
teaser:
tags: web,python,django
author: George Brocklehurst
published_on: 2013-02-22
---

I have a confession to make: Even though thoughtbot is mostly known for the work
we do with Ruby on Rails, I'm a huge Django fan. Someone left the keys to the
blog lying around, so I thought I'd take it for a quick joy ride around the one
of my favourite Django features: class-based generic views.

Firstly, just to help anyone who's not _au fait_ with Django terminology
catch up, a <dfn>view</dfn> deals with a request and does whatever needs to be
done to produce the correct response. If Rails is your thing, you can think of
it as being roughly equivalent to a controller action.

Class-based views were introduced in Django 1.3, but despite being around for a
couple of years they're still not as widely used as they should be.

## What came before

Before the class-based view there was the humble function-based view. It was a
simple time.  Views would take a request and return a response. Here's an
example for displaying a single blog post:

    def blog_post_detail_view(request, *args, **kwargs):
        """Displays the details of a BlogPost"""
        blog_post = get_object_or_404(BlogPost, pk=kwargs.get("pk"))
        return TemplateResponse(request, "blog/blog_post.html", {
            "blog_post": blog_post,
        })

The mechanism is undoubtedly simple, but the resulting code isn't. It's very
dense, with a single function loading the context data and building the
response. This small example isn't too bad, but imagine if we added comments,
and author information, and a list of related posts. We'd quickly get to
something very unwieldy.

## Class-based views

It would be great if we could encapsulate all of this logic in a class so that
it was less dense and easier to extend.  Conveniently Django 1.3 helps us to do
just that by providing a `View` class that we can extend. Here's our blog post
detail view, refactored into a class:

    class BlogPostDetailView(View):
        """Displays the details of a BlogPost"""

        def get(self, request, *args, **kwargs):
            return TemplateResponse(request, self.get_template_name(),
                                    self.get_context_data())

        def get_template_name(self):
            """Returns the name of the template we should render"""
            return "blog/blogpost_detail.html"

        def get_context_data(self):
            """Returns the data passed to the template"""
            return {
                "blogpost": self.get_object(),
            }

        def get_object(self):
            """Returns the BlogPost instance that the view displays"""
            return get_object_or_404(BlogPost, pk=self.kwargs.get("pk"))

The readability and extensibility have definitely improved, but the code could
still be better. There are a lot of small decisions encoded in the class that
needn't be here. In writing this code I had to decide what to call the template,
what to call the URL keyword argument that contains the model's primary key, and
what to call the context variable that is passed to template.  None of those
decisions were hard to make, but none of them really matter that much. What
matters is that they remain consistent between different views, so that
developers don't have to waste time looking for things only to discover that
this view doesn't quite work like the last one they used.

In a nutshell, what this code needs is conventions. Application level
conventions are good, but framework level conventions are better: The same
developer can quickly understand many applications and easily move between
projects or even companies without needing as much time to get up to speed. As
consultants, when we're working with new clients framework level conventions are
invaluable.  Rails is famed for its opinionated stance on convention over
configuration, while Django is generally quieter on the subject. This might lead
you to believe that Django doesn't provide any conventions, but there's more to
Django than meets the eye.

## Class-based generic views

Convention is where the "generic" part of "class-based generic views" comes into
play. Django provides subclasses of `View` for a variety of common
situations that are packed full of conventions and take all of those pesky
little decisions out of our hands.

Using the generic `DetailView`, which displays details of a single model
instance, we can boil the previous example down to a few simple lines:

    class BlogPostDetailView(DetailView):
        """Displays the details of a BlogPost"""
        model = BlogPost

## Convention and configuration

Of course, convention stops being helpful as soon as you want to do something
unconventional. That's where configuration comes into its own. Thankfully,
Django's class-based generic views provide both by using the [Template Method
pattern][template-method] which makes it very easy to customise each part of the
generic process.

Let's do something less conventional and update our `BlogPostDetailView` to only
display posts with a `published` flag set to `True`. We can do this by
providing the view with a `QuerySet` to use as the basis of its query:

    class BlogPostDetailView(DetailView):
        """Displays the details of a BlogPost"""
        model = BlogPost
        queryset = BlogPost.objects.filter(published=True)

Alternatively, we can go one step further and override the `get_queryset`
method and use different querysets based on the properties of the request:

    class BlogPostDetailView(DetailView):
        """Displays the details of a BlogPost"""
        model = BlogPost

        def get_queryset(self):
            if self.request.GET.get("show_drafts"):
                return BlogPost.objects.all()
            else:
                return BlogPost.objects.filter(published=True)

The code is still very concise and readable. Template Method has allowed us to
override one part of the algorithm without reimplementing the whole thing.  When
we return to this class in the future there aren't dozens of lines of
boilerplate code to read through to find the significant parts.

Behind the scenes, the `DetailView` class provides an implementation of `get`,
which in turn calls the `get_object` method. `get_object` is the template
method, so if we wanted to buck all of the conventions we could override it.
Since we're only concerned with changing the queryset, we can ignore the
template method itself and turn our attention to `get_queryset`, which is one of
the primitive operations that `get_object` uses. In the first example we take
advantage of the default implementation of `get_queryset`, which will return
`self.queryset` if we've provided it. In the second example we override
`get_queryset` entirely. In both cases we keep the rest of `get_object`'s
algorithm, including useful features like 404 Not Found responses and support
for looking up the model instance by primary key or using a slug.

## Knowing what to override

The downside isn't reading existing views, but writing new ones. The
documentation for this feature has improved significantly since Django 1.3 was
released, but there's still something of a learning curve. Of course, you're
going to end up learning a set of conventions for your application, so it may as
well be the one Django provides.

The good news is that these class-based generic views are very <abbr title="Test Driven Development">TDD</abbr> friendly. If
you are missing some required configuration attribute Django will raise an
`ImproperlyConfigured` exception with a helpful message outlining your options
(usually either setting an attribute, or overriding one or more methods that
depend on the missing attribute). There's rarely such a clear example of a
failing test telling you exactly what to do next.

There are also some very helpful resources out there to get you started and to
use as reference material when you're up and running:

1. [The official Django documentation on class-based views][docs-topic] for a more
   complete introduction than I've given here.
1. [The official class-based views reference][docs-ref] for specifics of
   individual classes and methods. In particular, this includes a "method
   flowchart" for each class which is helpful for figuring out what the
   Template Methods and their primitive operations are called.
1. [The source code for the `django.views.generic` module][source] is also a
   fun read.
1. [Classy class-based views][ccbv] is an alternative set of documentation,
   which is particularly useful as a quick reference.

## That's all folks

I hope you enjoyed this look inside Django. Maybe we can do it again sometime?

[template-method]: http://en.wikipedia.org/wiki/Template_method_pattern
[docs-topic]: https://docs.djangoproject.com/en/dev/topics/class-based-views/
[docs-ref]: https://docs.djangoproject.com/en/dev/ref/class-based-views/
[source]: https://github.com/django/django/blob/master/django/views/generic/__init__.py
[ccbv]: http://ccbv.co.uk
