Django FBVs and CBVs
How function-based and class-based views differ in behavior, the as_view and dispatch flow, and how to choose all the way up to generic views
Contents
Writing the same page as a function or as a class is a choice between reusability and readability.
What a view takes and what it returns
In Django, a view is a callable that takes an HTTP request object and returns a response object. Written as a Python function, it is a function-based view (FBV). Written as a class, it is a class-based view (CBV). Both produce the same response; the difference is in how the code is structured.
The axis of the choice is the trade-off between reusability and readability. A simple page reads better as a single function, while repeated similar pages benefit from class inheritance cutting duplication.
Function-based views
An FBV is a Python function that takes the request as an argument. Branching on the request method is written directly with an if request.method conditional.
from django.http import HttpResponse
def my_view(request):
if request.method == 'GET':
return HttpResponse('handle GET request')
elif request.method == 'POST':
return HttpResponse('handle POST request')The function above shows all branches in one place. Reading top to bottom tells you which method leads to which handling. That explicitness is the FBV's biggest strength, and the shorter the logic, the more it pays off. The cost is that once several similar functions exist, the shared parts have to be lifted out of the functions, and that takes real work.
Class-based views
A CBV defines the view as a class and uses HTTP method names directly as method names. Methods such as get and post handle their respective request methods.
from django.views import View
from django.http import HttpResponse
class MyView(View):
def get(self, request):
return HttpResponse('handle GET request')
def post(self, request):
return HttpResponse('handle POST request')The key point is that branching happens through method definitions rather than conditionals. You define only the methods you handle, and Django rejects undefined methods automatically. Inheritance and mixins let several views share common behavior, which helps reuse. The cost is that the flow scatters across methods and parent classes, which is excessive structure for a small page.
URLconf registration and as_view
The URLconf maps each path to a callable. An FBV is already a function and maps directly, but a CBV is a class and cannot. as_view() bridges that gap.
from django.urls import path
from . import views
urlpatterns = [
path('posts/', views.my_view, name='posts'), # FBV: the function itself
path('about/', views.MyView.as_view(), name='about'), # CBV: turned into a function by as_view()
]as_view() is a class method on View; calling it builds and returns a view function that will handle requests. From the URLconf's perspective, FBVs and CBVs both look like functions. The class structure hides inside that function.
How as_view and dispatch work
When a request arrives, the function built by as_view() instantiates the class and calls dispatch(). dispatch() lowercases the request method, finds the method of the same name, and runs it. For an undefined method, it routes to http_method_not_allowed and returns an HTTP 405 response. This flow is defined in Django's official documentation on class-based views and in the implementation of django.views.generic.base.View.
This is where the branching difference between FBVs and CBVs becomes visible. In an FBV the developer writes the branch with if; in a CBV, dispatch() branches on the method name instead. The list of allowed methods lives in the http_method_names attribute, so any method not on the list is blocked before a handler is looked up. You avoid writing branch code, but understanding the flow means knowing the parent class implementation.
Generic views and mixins
The practical benefit of CBVs comes from the generic views Django ships. Common page patterns such as list, detail, create, update, and delete are already implemented as classes. Specify a model and a template and the repetitive query and pagination code is handled for you.
from django.views.generic import ListView
from .models import Post
class PostListView(ListView):
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'
paginate_by = 10ListView handles querying the Post list, rendering the template, and splitting pages through inherited behavior. Setting paginate_by alone is enough to get pagination. Writing the same page as an FBV means repeating the query, paging, and rendering by hand.
Common behavior is composed in through mixins. A mixin is not a standalone view but a small class that adds capability to another view. A login check, for example, is handled by putting LoginRequiredMixin first.
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView
from .models import Post
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'body']
login_url = '/login/'Inheritance order matters for mixins. LoginRequiredMixin must come before CreateView so that the login check runs first in the method resolution order (MRO). Reversing the order can silently disable the check, so permission mixins always go on the left.
When to choose which
The two approaches produce the same result but differ in structure and cost.
| Aspect | FBV | CBV |
|---|---|---|
| Structure | Function | Class |
| Reusability | Difficult | Easy through inheritance and mixins |
| Readability | Simple and direct | Structured but with scattered flow |
| Extensibility | Complexity grows as features are added | Easy to extend through inheritance |
| Generic views | Must be implemented yourself | Use the generic views Django provides |
FBVs suit simple logic with no complicated handling. They fit small projects, quick prototypes, and a preference for a functional style.
CBVs win when the logic is complex and several request methods are involved. They pay off when common functionality must be shared across views, or when a growing project makes extensibility matter. Using generic views such as list and detail directly is another case where a CBV is the natural fit.
Summary
FBVs and CBVs produce the same behavior; what differs is whether the code is structured as a function or as a class. An FBV gathers branching into one conditional and reads easily, while a CBV lets dispatch() branch on method names and grows reuse through inheritance and mixins. The dividing criterion is the complexity of the page and how much it repeats: simple means FBV, and a repeating pattern or a matching generic view means CBV. Mixing both within one project is perfectly fine, so pick whichever makes each individual page simpler.