Django Best Practices: Function-Based Views vs Class-Based Views
Django's support of both function-based views and class-based views causes a lot of confusion for newcomers. Multiple ways to do the same thing? Which should I use?
Within Django a
views.py file adds logic to HTTP requests and responses. If we attempt to visualize how a Django webpage is displayed the process goes something like this:
- user enters in a URL like
urls.pyfile identifies the proper URL route
views.pyfile associated with that URL route handles the HTTP request, specifies the proper template, database model (if needed), and logic necessary, and then sends back an HTTP response to the user
This process happens over and over again for each webpage within a Django site.
Historically Django used only function-based views (FBV). These take a standard Python function form of accepting a
request object as an argument and returning a
Here is an example of a function that returns a simple template. For simplicity, no database models are included here.
# helloworld/views.py from django.shortcuts import render def homepageView(request): return render(request, 'helloworld/index.html')
Notice that the view accepts a single
request object as the argument and uses the built-in render shortcut to return a response. In this case, it displays the template
index.html within the
FBVs can often grow quite large in their size because all the logic for the view must be included. The upside of this approach is that there is no hidden behavior; you know exactly what you're getting. Decorators can be added, yes, but largely what you see is what you get.
The downside is length and repetition. How readable is a 15-line FBV? Not very in my opinion. Especially when probably 12 of the lines are doing the exact same work as other FBVs and really there's only 3 lines that are different.
Class-based Views (CBVs)
Starting with Django 1.3, class-based views were added and by leveraging inheritance, views code became much more concise. Also customizing an existing CBV is straight-forward; only the parts that have changed need to be updated. Goodbye repetition. And goodbye long, hard-to-read views.
What's the cost? You need to understand the underlying inheritance structure well before you start making changes. All the code is not as readily apparent as it is in a FBV which some developers find harder to reason about.
Generic Class-based Views (GCBVs)
GCBVs are wonderfully concise and powerful. However they can be hard to customize since that requires diving into their internals and really understanding the underlying methods and inheritance structure. Classy Class-Based Views is a wonderful resource for unpacking views in this way.
So which one to use? You can probably tell I have a bias towards CBVs in general. Personally, I always start with a GCBV if possible, drop down to a CBV if needed, and if I really must get granular with behavior I'll use a FBV on occasion. Most--but not all--Django developers I know follow a similar approach.
I do think, however, that once you've hit an intermediate level of comfort with Django, the ability to write the same view both as a FBV and as a CBV is valuable. Arguably it is easier from a learning perspective to see all the logic in one place via a FBV versus having to go through the inheritance chain of a CBV.
So FBVs have their place and it's a strength of Django, in my opinion, that both FBVs and CBVs are supported. Unfortunately this comes at the cost of some additional confusion for newcomers. Hence why I wrote this post :).