Last updated: April 2026 · AnomixLabs Technical Team
CBVs and FBVs are not rivals, but complements. A developer proficient in Django will use both—choosing the right tool for the job.
1. Function-Based Views (FBV)
An FBV is a view written as a Python function. It handles GET and POST requests by checking request.method. Its strengths lie in its simplicity and readability:
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from .models import Article
from .forms import ArticleForm
@login_required
def article_create(request):
if request.method == 'POST':
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save(commit=False)
article.author = request.user
article.save()
return redirect(article)
else:
form = ArticleForm()
return render(request, 'article_form.html', {'form': form})
2. Class-Based Views (CBV) — Generic Views
CBVs leverage Django's class hierarchy. Generic views provide standard CRUD patterns with minimal code:
from django.views.generic import (
ListView, DetailView, CreateView, UpdateView, DeleteView
)
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
class ArticleListView(ListView):
model = Article
template_name = 'article_list.html'
context_object_name = 'articles'
paginate_by = 10
ordering = ['-created_at']
class ArticleDetailView(DetailView):
model = Article
slug_field = 'slug'
class ArticleCreateView(LoginRequiredMixin, CreateView):
model = Article
fields = ['title', 'content']
login_url = '/login/' # Assuming '/login/' is the login URL
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class ArticleDeleteView(LoginRequiredMixin, DeleteView):
model = Article
success_url = reverse_lazy('article_list')
3. Overriding get_queryset and get_context_data
class ArticleListView(ListView):
model = Article
def get_queryset(self):
# Fetch only published articles, avoiding N+1
return Article.objects.filter(
is_published=True,
).select_related('author', 'category')\
.prefetch_related('tags')
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['total_count'] = self.get_queryset().count()
ctx['categories'] = Category.objects.all()
return ctx
4. The Mixin MRO Trap
When using multiple mixins, Python's Method Resolution Order (MRO) is critical. LoginRequiredMixin should always come first:
# CORRECT — LoginRequiredMixin first
class ArticleCreateView(LoginRequiredMixin, CreateView):
...
# INCORRECT — MRO conflict, login redirect won't work
class ArticleCreateView(CreateView, LoginRequiredMixin):
...
# To see the order:
print(ArticleCreateView.__mro__)
5. Writing Custom Mixins
class AuthorRequiredMixin:
"""Mixin that only allows the object's owner to access."""
def get_object(self):
obj = super().get_object()
if obj.author != self.request.user:
from django.core.exceptions import PermissionDenied
raise PermissionDenied
return obj
class ArticleUpdateView(LoginRequiredMixin, AuthorRequiredMixin, UpdateView):
model = Article
fields = ['title', 'content']
6. Custom Form Handling with FormView
from django.views.generic.edit import FormView
class SearchView(FormView):
template_name = 'search.html'
form_class = SearchForm
success_url = '.' # Return to the same page
def form_valid(self, form):
query = form.cleaned_data['query']
# Perform search, save to session, etc.
return super().form_valid(form)
7. When to Use Which?
| Scenario | Recommendation | Why? |
|---|---|---|
| Standard CRUD | CBV | Generic views reduce code |
| Complex business logic | FBV | Linear flow is easier to read |
| Webhook / API endpoint | FBV | Single function, simple pattern |
| Pages requiring login | CBV (Mixin) | LoginRequiredMixin is reusable |
| Repetitive patterns | CBV | DRY with mixins |
8. Writing Tests: FBV vs CBV
from django.test import TestCase, Client
from django.contrib.auth import get_user_model
User = get_user_model()
class ArticleViewTests(TestCase):
def setUp(self):
self.user = User.objects.create_user('ali', password='pass')
self.client = Client()
def test_list_view(self):
response = self.client.get('/articles/') # Assuming '/articles/' is the URL for ArticleListView
self.assertEqual(response.status_code, 200)
def test_create_requires_login(self):
response = self.client.get('/articles/new/') # Assuming '/articles/new/' is the URL for ArticleCreateView
self.assertRedirects(response, '/login/?next=/articles/new/') # Assuming '/login/' is the login URL
def test_create_logged_in(self):
self.client.login(username='ali', password='pass')
response = self.client.get('/articles/new/')
self.assertEqual(response.status_code, 200)
9. URL Structure Comparison
from django.urls import path
from . import views # Assuming views.py contains the FBV and CBV definitions
urlpatterns = [
# FBV
path('new/', views.article_create, name='article_create'),
# CBV — .as_view() is required
path('', ArticleListView.as_view(), name='article_list'),
path('/', ArticleDetailView.as_view(), name='article_detail'), # Assuming slug is used for detail view
path('/delete/', ArticleDeleteView.as_view(), name='article_delete'), # Assuming slug is used for delete view
]
Summary
Know both approaches and choose based on the situation. CBVs are suitable for standard CRUD, repetitive patterns, and when mixins are needed. FBVs are better for complex business logic, webhooks, and single-function views. Pay attention to MRO order when using mixins—LoginRequiredMixin should always be first. Writing tests is similarly straightforward for both approaches.

Frequently Questions
Why does mixin order matter? expand_more
How do I debug a CBV? expand_more
How do I customize form fields in CreateView? expand_more
When should I prefer ViewSet? expand_more
Does FBV or CBV run faster? expand_more
What is the difference between CBV and TemplateView? expand_more
How is permission_required used in a CBV? expand_more
Ali Kasımoğlu
Full-stack Developer & Founder of AnomixLabs
A software developer specializing in the Python and Django ecosystem. Focuses on modern web architectures, AI integrations, and minimalist user experiences. Under the AnomixLabs umbrella, he aims to transform complex problems into lean and effective digital solutions.