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.
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.
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…
… 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'}
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.
My name is Reinout van Rees and I program in Python, I live in the Netherlands, I cycle recumbent bikes and I have a model railway.
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):