Django class based view walkthrough: TemplateView

Tags: django, plone

In my previous entry I showed a simple TemplateView example. I’ll make it even simpler. views.py:

from django.views.generic.base import TemplateView


class SomeView(TemplateView):
    template_name = 'my_app/someview.html'

    def get_context_data(self, **kwargs):
        return {'name': 'reinout}

And in urls.py:

import ...

urlpatterns = patterns(
    ...
    url(r'^class_based_view/$',
        my_app.views.SomeView.as_view(),
        name='class_based_view'),

As you can probably see, there are two things you need to somehow know when subclassing from TemplateView:

  • Add a template_name attribute on the class.
  • Add a .get_context_data() method that returns the context dictionary.
  • In your urls.py, import the class and append .as_view().

“You need to know something somehow” is perfectly fine: that’s what documentation is for. You need to know a lot of those things, like that models go into models.py and templates into a templates/ subdirectory, for instance.

For better understanding, let’s dive into the django code and take a detailed look at what happens. That way, we can better understand what happens and that way we can make our own view and perhaps our own mixins.

First things first: open django/views/generic/base.py. Or look at it online in a second tab.

class View

View is the base class for all views. It takes care of two things that you should know about:

  • The .as_view() method you call on the view in your urls.py.
  • It calls .get() for a GET request, .post() for a POST request and so on.

In your urls.py, you need to import the class and feed it to your urls with a .as_view() appended. You might ask why that .as_view() is needed. It is needed because you want your view objects to be thread-safe. That’s an expensive word for “self.bla_bla = 3 should be safe to use and self.bla_bla should not end up as a value in another view instance”. The way urls.py works (it expects callables) wouldn’t work nicely with classes. Anyway, that’s High Python magery. Nothing to worry about. Just accept that the .as_view() is a pretty fine and decent solution :-)

You can even pass keyword arguments to as_view() which will be set as attributes on your view objects. So YourView(template_name='my_view.html') will give your view a self.template_name with the value 'my_view.html'. Provided your view had some default value for that attribute, otherwise you’d get an error: nicely explicit.

Behind the scenes, what as_view() returns is the .dispatcher() method that basically just calls .get() for a GET request, .post() for a POST request and so on. Of course it passes along arguments and keyword arguments that your url config wishes to pass along.

Now, you don’t need to worry about that dispatch method. You won’t ever have to customize that one. Likewise the as_view method. Remember the two customization points:

  • If your view class always has some attribute (self.template_name will be a common one), you can customize that right in your urls.py by passing it a long as a parameter (.as_view(template_name='something.html')).
  • Args and keyword args you defined in your urls.py get passed just fine to the get()/post()/delete()/head() whatever.

class TemplateResponseMixin

Hey, our first mixin! Python support multiple inheritance. So your view can inherit from more than one class. This way, you can mix in one or two methods or attributes into your class by inheriting from an extra mixin class.

Warning: using mixins looks fine on the first sight, but experience has shown that debugging can quickly become an utter hell as you have to look in 10 different spots before you figure out which method does what to your hapless data... (8 years of zope/plone experience speaking here!)

Anyway... TemplateResponseMixin mixes in two really important things:

  • A template_name attribute. The value of this gets used as the template. (Actually, it is a little bit more involved: there’s a .get_template_names() that returns the value of template_name, but that’s not something you’ll commonly customize).
  • A render_to_response() method that accepts a context dictionary and renders the template with that context. Behind the scenes, it wraps the context dictionary automatically in a RequestContext so that all the so-called context processors are active. Not wrapping the context in a RequestContext is a common error (“hey, my STATIC_URL variable seems to be empty in my templates!), so getting it handled automatically is great.

As it is a mixin, this render_to_response() isn’t actually called anywhere within the class itself. So onwards to...

class TemplateView

... TemplateView inherits from both View and TemplateResponseMixin. It is indended just for rendering a template with a context. Technically, it means responding to a GET request and returning a response.

What you need to know about TemplateView itself:

  • It has a .get_context_data() method that you ought to implement in your subclass. It should return a context dictionary. So implement this method and the dictionary you return will be given to your template. Note that this method gets passed any keyword arguments you defined in your url.
  • It implements the .get() method that View’s dispatcher will call when it gets a GET request. This one calls .get_context_data() to get the context and feeds it to TemplateResponseMixin’s .render_to_response(). This happens behind the scenes and you shouldn’t ordinarily have to customize it.

So. TemplateView is useful by itself. For many views, this is what you ought to be subclassing. Despite the many layers and methods-calling-other-methods that happens, there are just two items you need to implement in your subclass:

  • The template_name attribute that points at your template.
  • The .get_context_data() that ought to return a context dictionary.

Just for clarity, here’s the example view again:

from django.views.generic.base import TemplateView


class SomeView(TemplateView):
    template_name = 'my_app/someview.html'

    def get_context_data(self, **kwargs):
        return {'name': 'reinout'}

Conclusion

In the end, the TemplateView class is pretty simple when you actually use it.

The underlying mechanism is relatively elaborate and depends quite a lot on every part doing exactly what it has to do. And it consists out of quite some parts. So I hope the complexity you can build into it doesn’t grow out of hand.

Getting the RequestContext automatically is a big plus for TemplateView.

Have fun writing class based views! I’ll probably write more about them in the weeks to come as I discover more things. There are handy views for redirecting, for handling forms and so on. Lots to discover.

blog comments powered by Disqus
 
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):