No need to use get_context_data, use {{ view.some_method }}

Tags: django

There’s a super nice super elegant productivity trick for Django class based views. Something that is not very well know, as I discovered at last week’s djangocon. So… time to share it!

The problem? Getting information into your template’s context. What most people do is to overwrite get_context_data(), just like the example from the django 1.5 docs

def get_context_data(self, **kwargs):
    # Call the base implementation first to get a context
    context = super(PublisherDetailView, self).get_context_data(**kwargs)
    # Add in a QuerySet of all the books
    context['book_list'] = Book.objects.all()
    return context

In the template you’d use it like this:

{% for book in book_list %}
    ...
{% endfor %}

The solution? A new feature in Django 1.5 : Django’s generic class based views now automatically include a view variable in the context. This variable points at your view object. So… you basically never need to modify get_context_data() anymore! Example:

from django.views.generic import TemplateView

class SomethingView(TemplateView):
    template_name = "something.html"
    title = "My beautiful list of books"

    def books(self):
        return Book.objects.all()

    # Note: no get_context_data() here!

The template then uses the automatic view variable to grab the list of books and the title:

<h1>{{ view.title }}</h1>
<ul>
  {% for book in view.books %}
    <li>{{ book }}</li>
  {% enfor %}
<ul>

Isn’t that wonderful? It fits Django’s philosophy perfectly: the templates should be dumb, the processing should happen inside the view. Now it is dead easy to “just add a method” to calculate something, the method will automatically be available in the template as view.your_quick_helper_method.

No more tedious duplication like this:

from django.views.generic import TemplateView

class SomethingView(TemplateView):

    def books(self):
        return Book.objects.all()

    def get_context_data(self, **kwargs):
        context = super(SomethingView, self).get_context_data(**kwargs)
        context['books'] = self.books()
        # ^^^ bad duplication.
        return context

I mean, context['books'] = self.books(), that’s just plain unnecessary duplication. But… no need to do it anymore! Just call the method or grab the attribute directly.

Some notes:

  • It works for Django’s generic class based views. So if you subclass TemplateView or EditView or so, you’re fine.

    Behind the scenes, it is the ContextMixin that provides the basic get_context_data() method that injects the view variable into the context.

  • Django’s normal view restrictions apply. Attributes are fine. Methods with just self as a parameter are fine.

    Methods that need an extra parameter (def books(self, category)) won’t work, as django’s template language doesn’t allow you to call methods like that.

    Similarly, attributes and methods prefixed with an underscore (“private”) aren’t available.

  • Hm, I think this is an essential (and very handy! part of Django’s view mechanism. I added the original ticket. But… I notice now that it is hardly mentioned in the docs.

    Should this need to be better documented? I’m thinking about changing the adding extra context paragraph to “just stick a method/attribute on the view” instead of “mess with get_context_data()”. Is this a good idea?

 
vanrees.org logo

About me

My name is Reinout van Rees and I work a lot with Python (programming language) and Django (website framework). I live in The Netherlands and I'm happily married to Annie van Rees-Kooiman.

Weblog feeds

Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):