Understanding Django Class Based Views – part 1: the View class

Written by Johannes on September 21, 2012 Categories: Django

Your ads will be inserted here by

Easy AdSense.

Please go to the plugin admin page to
Paste your ad code OR
Suppress this ad slot.

Starting with Version 1.3, Django ships with a very powerful class based view system. Django’s (generic) class based views provide a lot of functionality (e.g. List views and Detail views) out of the box. Adapting these views to your own purposes may seem a bit challenging and overwhelming as Django’s documentation throws you headfirst into the water and hides a lot of the magic involved in Django’s class based views and mixin system. Trying to backtrace all the class inheritances involved and the methods flows of a request to the view may confuse you if you are trying to start from understanding what’s going on in the ListView.

Therefore, to understand Django’s system of class based views we start out with the View class, the base class from which all other class based views arederived and work our way up to the more complex classes.

A little disclaimer: I’m writing this tutorial to teach myself just as much as to explain CBV to others. So if you find something to be incomplete or even wrong, don’t tear into me but give me a friendly comment and I will improve it.

This class can be found in django/views/generic/base.py and exhibits the following methods and attributes (read the documentation here: https://docs.djangoproject.com/en/dev/ref/class-based-views/base/):

class View(object):
    """
    Intentionally simple parent class for all views. Only implements
    dispatch-by-method and simple sanity checking.
    """
 
    http_method_names = ['get', 'post', 'put',
                         'delete', 'head', 'options', 'trace']
 
    def __init__(self, **kwargs):
        """
        Constructor. Called in the URLconf; can contain helpful extra
        keyword arguments, and other things.
        """
        # [...]
 
    @classonlymethod
    def as_view(cls, **initkwargs):
        """
        Main entry point for a request-response process.
        """
        # [...]
 
        def view(request, *args, **kwargs):
        # [...]
            return self.dispatch(request, *args, **kwargs)
 
        # [...]
 
        return view
 
    def dispatch(self, request, *args, **kwargs):
        # [...]
        handler = getattr(self,
                          request.method.lower(),
                          self.http_method_not_allowed)
        return handler(request, *args, **kwargs)
 
    def http_method_not_allowed(self, request, *args, **kwargs):
        # [...]
 
    def options(self, request, *args, **kwargs):
        # [...]
 
    def _allowed_methods(self):
        # [...]

This is obviously a simplified and abbreviated version of the class. But let’s see what happens when we take the class to the test and use it as the base of our view class:

# myapp/views.py
 
from django.views.generic import View
 
class MyView(View):
    pass
 
# myproject/urls.py
 
from django.conf.urls import patterns, include, url
from myapp.views import MyView
 
urlpatterns = patterns('',
    url(r'^myview/', MyView.as_view(), name='myview'),
)

View.as_view() returns a callable view function that forwards the request to View.dispatch(). View.dispatch() will try to provide an appropriate handler for the request method.

Your ads will be inserted here by

Easy AdSense.

Please go to the plugin admin page to
Paste your ad code OR
Suppress this ad slot.

With the current configuration a request to http://myserver/myview/ is met with an Error 405 – METHOD NOT ALLOWED response.

This may seem disappointing right now, but it is actually just what we should have expected. View.dispatch() will look for an attribute of the View class that coincides with the request type. In our case we have a get request, so View.dispatch() tries to deliver an attribute MyView.get. As this does not exists it falls back and delivers the http_method_not_allowed method.

We’ll get our view to respond more upliftingly very soon. All we need to do is provide an appropriate handler for our MyView class. So let’s try something very simple.

# myapp/views.py
 
class MyView(View):
 
    def get(self, request, *args, **kwargs):
        pass

Point your browser to the view’s url again and you will see Django’s pleasantly colored and nicely formatted debug display scolding you for not returning a HttpResponseObject with your view. A cure for this remedy can easily provided as we still remember HttpResponseObjects from our day one of working with Django:

# myapp/views.py
 
from django.http import HttpResponse
 
class MyView(View):
 
    def get(self, *args, **kwargs):
        return HttpResponse('Hello handsome!')

So we now got our MyView class to respond nicely and upliftingly to our requests. But of course we want our web apps to do more than just tap our egos by complimenting our good looks. For this purpose we will look at the TemplateView and TemplateResponseMixin in the next installment of this series.

From this point on we can already use this view similarly to the method-based view we know from earlier versions of Django, even though this will deprive us of many of the advantages offered to us by Django’s class based view system.

1 Comment

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre user="" computer="" escaped="">