biotech Django & Python

Django CBV vs FBV: Complete Comparison of Class and Function-Based Views

AK
Ali Kasımoğlu
18 Apr 2021 schedule 4 min read
Django CBV vs FBV Complete Comparison - AnomixLabs
analytics

Insight Density

groups Target Audience: Intermediate
65 Score

Calculated by technical complexity and content density.

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.

Django Class-Based Views and Function-Based Views usage diagram

Frequently Questions

Why does mixin order matter? expand_more
Python's Method Resolution Order (MRO) determines which method is called first when multiple base classes define the same method. If LoginRequiredMixin comes last, dispatch() from the view class runs first — skipping the auth check. Mixins that modify dispatch() must come before the view class in the inheritance list. Use ccbv.co.uk to visualize MRO for any CBV.
How do I debug a CBV? expand_more
CBVs can feel opaque. Debug tools: 1) Visit ccbv.co.uk — it visualizes every CBV's methods and MRO. 2) In the Django shell: print(ArticleListView.__mro__) to see the resolution order. 3) Add print statements inside get(), post(), or get_queryset() to trace execution. 4) Django Debug Toolbar's Template panel shows which context variables are available in the template.
How do I customize form fields in CreateView? expand_more
fields = ['title', 'content'] covers simple cases. For a custom form class, specify form_class = ArticleForm. Override get_form() to modify form behavior dynamically, or get_initial() to pre-populate fields. To validate additional business rules before saving, override form_valid() and call super() only after your checks pass.
When should I prefer ViewSet? expand_more
ViewSet is specific to Django REST Framework and is used for API endpoints, not HTML views. It auto-generates URLs via Router. Use ModelViewSet for full CRUD API on a single model; use ReadOnlyModelViewSet when you only need list and retrieve; use a plain ViewSet for non-standard actions. For HTML views, stick with CBV or FBV — ViewSet does not render templates.
Does FBV or CBV run faster? expand_more
The performance difference is negligible in practice. Both run a similar amount of Python code to process a request. Real performance bottlenecks are: database queries (N+1 problems), cache usage, and external API calls. Choose FBV vs CBV based on code clarity and reuse requirements, not performance.
What is the difference between CBV and TemplateView? expand_more
TemplateView is the simplest CBV — it renders a template with no model or form. Perfect for static pages (About, FAQ). Override get_context_data() to pass data to the template. For pages that only need to display database content without forms or complex logic, TemplateView with an annotated queryset in the context is often cleaner than a full ListView.
How is permission_required used in a CBV? expand_more
Use PermissionRequiredMixin: class StaffView(PermissionRequiredMixin, ListView): permission_required = 'blog.view_article'. For multiple permissions: permission_required = ('blog.view_article', 'blog.change_article'). Set raise_exception = True to return a 403 instead of redirecting to login for authenticated users who lack the required permission.
Tags: #Django #CBV #FBV #Class-Based Views #Function-Based Views #ListView #Mixin #MRO #ViewSet
share

Share This Article

Support us by sharing this with your network.

AK

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.

psychology
psychology

Ask a Question About the Article

AnomixAI · Answers based on the article content

5 questions left
Only about article content 0/500
forward_to_inbox

The Future Decoded.

Join 5,000+ engineers and founders receiving the monthly briefing on enterprise AI, software architecture and digital transformation. No spam.