If you follow my content here in the blog, you probably have already noticed that I’m a big fan of function-based views. Quite often I use them in my examples. I get asked a lot why I don’t use class-based views more frequently. So I thought about sharing my thoughts about that subject matter.
Before reading, keep that in mind: class-based views does not replace function-based views.
I’m not an old school Django developer that used it when there was only function-based views and watched the class-based views been released. But, there was a time that only function-based views existed.
I guess it didn’t take long until all sort of hacks and solutions was created to extend and reuse views, make them more generic.
There was a time that function-based generic views was a thing. They were created to address the common use cases. But the problem was that they were quite simple, and was very hard to extend or customize them (other than using configurations parameters).
To address those issues, the class-based views was created.
Now, views are always functions. Even class-based views.
When we add them to the URL conf using the
View.as_view() class method, it returns a function.
Here is what the
as_view method looks like:
Parts of the code was omitted for clarity, you can see the full method on GitHub.
So if you want to explicitly call a class-based view here is what you need to do:
To make it feel more natural, you can assign it to a variable:
The view function returned by the
as_view() method is outer part of every class-based view. After called, the view
pass the request to the
dispatch() method, which will execute the appropriate method accordingly to the request
type (GET, POST, PUT, etc).
Class-Based View Example
For example, if you created a view extending the
django.views.View base class, the
dispatch() method will handle
the HTTP method logic. If the request is a POST, it will execute the
post() method inside the view, if
the request is a GET, it will execute the
get() method inside the view.
Function-Based View Example
In function-based views, this logic is handled with if statements:
Those are the main differences between function-based views and class-based views. Now, Django’s generic class-based views are a different story.
Generic Class-Based Views
The generic class-based-views was introduced to address the common use cases in a Web application, such as creating new objects, form handling, list views, pagination, archive views and so on.
They come in the Django core, and you can implement them from the module
They are great and can speed up the development process.
Here is an overview of the available views:
Simple Generic Views
You can find more details about each implementation in the official docs: Built-in class-based views API.
I find it a little bit confusing, because the generic implementations uses a lot of mixins, so at least for me, sometimes the code flow is not very obvious.
Now here is a great resource, also from the Django Documentation, a flattened index with all the attributes and methods from each view: Class-based generic views - flattened index. I keep this one in my bookmarks.
The Different Django Views Schools
Last year I got myself a copy of the Two Scoops of Django: Best Practices for Django 1.8 book. It’s a great book. Each chapter is self-contained, so you don’t need to read the whole book in order.
School of “Use all the generic views”!
This school of thought is based on the idea that since Django provides functionality to reduce your workload, why not use that functionality? We tend to belong to this school of thought, and have used it to great success, rapidly building and then maintaining a number of projects.
School of “Just use django.views.generic.View”
This school of thought is based on the idea that the base Django CBV does just enough and is ‘the True CBV, everything else is a Generic CBV’. In the past year, we’ve found this can be a really useful approach for tricky tasks for which the resource-based approach of “Use all the views” breaks down. We’ll cover some use cases for it in this chapter.
School of “Avoid them unless you’re actually subclassing views”
Jacob Kaplan-Moss says, “My general advice is to start with function views since they’re easier to read and understand, and only use CBVs where you need them. Where do you need them? Any place where you need a fair chunk of code to be reused among multiple views.”
Excerpt from “Two Scoops of Django: Best Practices for Django 1.8” - 10.4: General Tips for Django CBV, page 121.
The authors said in the book they are in the first school. Personally, I’m in the third school. But as they said, there is no consensus on best practices.
PS: The authors released an updated version of the book, covering Django 1.11 and many updates: Two Scoops of Django 1.11: Best Practices for the Django Web Framework
Pros and Cons
For reference, some pros and cons about function-based views and class-based views.
There is no right or wrong. It all depends on the context and the needs. As I mentioned in the beginning of this post, class-based views does not replace function-based views. There are cases where function-based views are better. In other cases class-based views are better.
For example, if you are implementing a list view, and you can get it working just by subclassing the
overriding the attributes. Great. Go for it.
Now, if you are performing a more complex operation, handling multiple forms at once, a function-based view will serve you better.
The reason why I use function-based views often in my post examples, is because they are way easier to read. Many readers that stumble upon my blog are beginners, just getting started. Function-based views communicate better, as the code flow is explicit.
I usually always start my views as function-based views. If I can use a generic class-based view just by overriding
the attributes, I go for it. If I have some very specific needs, and it will replicate across several views, I create
my own custom generic view subclassing the
Now a general advice from the Django documentation: If you find you’re struggling to implement your view as a subclass of a generic view, then you may find it more effective to write just the code you need, using your own class-based or functional views.