Tuesday, May 31, 2011

Django Class-based Views

Since the release of Django 1.3, developers can choose to use class-based views in their web apps. Since the announcement of class-based views, there has been said a lot about them. As with all changes, there are pros and cons, people who are excited and people who are disappointed. I, and I guess a lot of people with me, are excited by the class-based views, but disappointed by the documentation Django gives with them. Time to try to clear things up.

What do I like about Django's class-based views?

Well, to start with: consistency. It might sound a bit lame, but I think it's a great thing that, following models and forms, views are now also part of the class-based club. I somehow always thought it was weird to write your forms and models in a class, but your views in a function. This weird feeling is now gone. Lucky me.

Secondly: consistency. Again? Yes, again, because you can now force your own code to be more consistent, by using subclassing. You can, for example, write your own superclass view template and let all your other views subclass it.

And last: Subclassing. I found it hard to find an example to make my point, but it might be obvious that subclassing is a good thing in general. If you have two slightly different views, subclassing reduces the amount of code that you will copy/paste and adapt a little, and thus reduces redundancy. Positivity all over the place!

What I don't like about Django's class-based views?

I have mentioned it before in this post: The documentation. Whenever you Google or Bing or whatever for Django class-based views, you will encounter a bunch of pages about Django's class-based generic views. It may be something personal, but I don't really like generic views. Whenever I use them, it feels like I am losing control over the website I am trying to create.

Moreover, the Django documentation seems to put a lot of custom logic, like what templates to use, in the URL config. Which is another thing that rings my developer alarms. The URL config, in my opinion, is a bunch of rules that tells my application what view should be called on what URL. Taking away some of the functionality of the views and putting it into the URL config just doesn't feel right.

So we don't want the class-based generic views, but ordinary class-based views. The way we used to have views in Django 1.2.x, but now with classes. So we Google and Bing and whatever along, to find any documentation covering ordinary class-based views. My Google skills are not something to brag about, but I could not find any information about non-generic class-based views.

So what do we do now?

I talked this over with one of my colleagues. He agreed with me about the lack of non-generic class-based views documentation and the fact that Django putting logic into the URL config is kind of ugly. He told me how he uses the class-based views in his projects and I decided to go with it, just because it made some sense to me.

In my current projects, all of my views are subclassing django.views.generic.View and optionally django.views.generic.base.TemplateResponseMixin. That way you can easily convert your former function-based views to new and cool class-based views. A code example to summarize and conclude.

class SomeFormView(TemplateResponseMixin, View):
    template_name = 'some_form.html'

    def get(self, request):
        form = SomeForm()

        return self.render_to_response({
            'form': form,
        })

    def post(self, request):
        form = SomeForm(request.POST)

        if form.is_valid():
            form.save()
            messages.success(request, 'Your form has been saved!')

        return self.render_to_response({
            'form': form,
        })


class AjaxThingView(View): 
    # Note that I don't subclass the TemplateResponseMixin here!

    def get(self, request):
        return HttpResponse(status=404)

    def post(self, request):
        id = request.POST.get('id')

        # Do something with the id
        return HttpResponse('some data')

8 comments:

  1. If you don't want view logic in your URLS - don't put it there! Put a one liner in your views.py:

    my_view = login_required(MyView.as_view())

    and use it like a normal view in your URLs. Putting it in the URLconf is just a shortcut.

    I really don't understand what you are wanting in terms of 'non-generic' class based views. The provided ones are 'generic' in that they provide some functionality which is 'generic' i.e. not specific to some situation. With classes, the generic functionality is provided by a class that can be subclassed, rather than a function that can be called.

    So it's extremely difficult to understand what you mean by a 'non-generic' class - that sounds like a class that provides no generic functionality, which is essentially pointless, unless you just like putting things in classes for some reason. If you want a non-generic class based view, here is one:

    class NonGenericClassBasedView(object): pass

    And here is the documentation for it:

    ReplyDelete
  2. Great post. I like the class based views to but currently have a mix between them and normal function views. I generally go with the class views if I would like to use the same view logic for different scenarios because, obviously, class's make that easy.

    I do agree on the documentation part. There is a lot of confusion on class views including people who just don't like them. I think most of that dislike and confusion is due to the lack of documentation.

    ReplyDelete
  3. What I mean by non-generic class-based views, is that I don't want to subclass all kinds of different View and Mixin objects to finally reach my goal. I want to have a straight on method to create my Views and don't want to be attached to all kinds of 'handy' built-in stuff, Django has thought of for me. Because most time, the built-in stuff just slightly differs from what I want.

    Hope that makes things a little more clear.

    ReplyDelete
  4. ohhhhhhh. i LIKE this style of using class based views.

    I totally agree on the documentation bit. It's just not there. the concept fo the get and post methods is barely there at all, and it's hidden.

    ReplyDelete
  5. Nice article, it's good to see someone praising the new approach rather than tearing it down.

    Two things about your code I take (very small) issue with. Don't use render_to_response anymore. Use render(), which passes a RequestContext implicitly. See this: https://docs.djangoproject.com/en/1.3/topics/http/shortcuts/#render

    The other issue, is how you do your form processing. The built-in FormView is wonderful! It provides a very standard way for rendering and processing a form. Is there a particularly good reason that you're not using FormView?

    ReplyDelete
  6. Thanks for the heads-up on the render shortcut, I didn't know it existed. But it seems it requires a template argument. When using class-based views, you already defined your template in the view class template_name variable.

    The reason I'm not using FormView is that it is one of many built-in views. There are good reasons to use some of them, if not all of them, but that is exactly what I don't like about the class-based views.
    My goal was to write a post about how to use class-based views, without all kinds of generic built-in subclasses of the original View class.

    ReplyDelete
  7. I have been using class based views since 2009. To help me do this, I had written a small wrapper over django. I call it Shiv and I plan to release it soon (There is a lot of coupling between the wrapper and the projet for which I made it). You can read more about it here http://t.co/VOQd0jF

    This is first in a series of posts that I intend to write.

    ReplyDelete
  8. I made a website that helps you see what's actually going on in the code. Essentially, it flattens the class hierarchy so that you can see all the methods and attributes defined on each leaf of the class tree.

    I expect django's class based views documentation will catch up soon enough, and it's still a work in progress, but I think it's turned out to be a pretty good reference :)

    I called it Classy Class Based Views :) http://ccbv.co.uk/

    ReplyDelete