Source

DjangoCon 2011 Notes / docs / class_based_views.rst

Class Based Views

The Old Way

You probably use one of these two ways for making a view that renders a form and then validates it on submit:

@login_required
def awesome_view(request):
    if request.method == "POST":
        form = AwesomeForm(request.POST)

        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('awesome'))
        else:
            form = AwesomeForm()

    return render(“awesome.html”, {'form': form})

Or:

@login_required
def awesome_view(request):
    form = AwesomeForm(request.POST or None)

    if form.is_valid():
        form.save()
        return HttpResponseRedirect(reverse('awesome')

    return render("awesome.html", {'form': form})

These both work and they're fine but you often have to repeat yourself and you have to deal with if conditions when debugging. Not much fun, not very clean.

The New Way

So, here's a class-based way of doing it:

Class MyAwesomeView(TemplateView):
    template_name = "awesome.html"

    def get(self, request):
        form = AwesomeForm()
        return self.render_to_response({'form': form})

    def post(self, request):
        form = AwesomeForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('awesome'))

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

You have a template_name variable at the top that specifies the template for the view.

Below that is the get() method that handles all GET requests. Typically it just takes self and request but if your route requires more arguments, they'll need to be specified here, too.

And, surprise, surprise, below that is post() that takes care of your POST requests. Same arguments as get() since it has to be hit by the same route.

What about @login_required?

True, we don't have @login_required on our methods and it won't work if you just add it there. What you can do, though, is wrap the dispatch() method and that will wrap all methods from that view with whatever decorators you provide. Example below:

@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
    return super(MyAwesomeView, self).dispatch(*args, **kwargs)

Add this into your class and you should be safe from all those nasty unauthenticated users.

URLs

How do you add these views to your URLs? Glad you asked:

from project.views import MyAwesomeView

[...]
url(r’^awesome/$’, MyAwesomeView.as_view(), name=”awesome”)
[...]

If you really hate having to import each view, at the bottom of your views.py file, you can do something like: awesome_view = MyAwesomeView.as_view() and then set your URLs view as app.awesome_view. I don't think this is needed, but to each his/her own.

Thanks and I hope that helps everyone get a small grip on class-based views. There are many other class-based views available, such as FormView for handling form rendering and validation (so this entire method wouldn't be needed) but there are very little docs for them. Let's write them!