CBV cheatsheet.

Generic list

from django.views.generic import ListView

class PostListView(ListView):
    model = Post
    template_name = "blog/list.html"
    context_object_name = "posts"
    paginate_by = 20
    
    def get_queryset(self):
        qs = super().get_queryset()
        return qs.filter(published=True).select_related("author")
    
    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["tags"] = Tag.objects.all()
        return ctx

Generic detail

from django.views.generic import DetailView

class PostDetailView(DetailView):
    model = Post
    template_name = "blog/detail.html"
    slug_field = "slug"
    slug_url_kwarg = "slug"

/posts/<slug:slug>/ → matches <slug:slug> url kwarg.

CreateView / UpdateView / DeleteView

from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy

class PostCreateView(CreateView):
    model = Post
    fields = ["title", "body"]
    success_url = reverse_lazy("blog:list")
    
    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)

class PostUpdateView(UpdateView):
    model = Post
    fields = ["title", "body"]
    template_name = "blog/post_form.html"   # same as create by default

class PostDeleteView(DeleteView):
    model = Post
    success_url = reverse_lazy("blog:list")

TemplateView / RedirectView

from django.views.generic import TemplateView, RedirectView

class AboutView(TemplateView):
    template_name = "about.html"

class HomeRedirect(RedirectView):
    url = "/posts/"
    permanent = False

FormView

from django.views.generic.edit import FormView
from .forms import ContactForm

class ContactView(FormView):
    template_name = "contact.html"
    form_class = ContactForm
    success_url = "/thanks/"
    
    def form_valid(self, form):
        form.send_email()
        return super().form_valid(form)

LoginRequiredMixin / PermissionRequiredMixin

from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin, UserPassesTestMixin

class MyView(LoginRequiredMixin, ListView):
    login_url = "/login/"
    redirect_field_name = "next"

class AdminView(PermissionRequiredMixin, ListView):
    permission_required = ["blog.delete_post"]

class OwnerView(UserPassesTestMixin, UpdateView):
    def test_func(self):
        return self.get_object().author == self.request.user

Custom mixin

class AjaxOnlyMixin:
    def dispatch(self, request, *args, **kwargs):
        if not request.headers.get("x-requested-with") == "XMLHttpRequest":
            return HttpResponseBadRequest()
        return super().dispatch(request, *args, **kwargs)

class MyView(AjaxOnlyMixin, ListView):
    ...

ContextMixin

class TitleMixin:
    title = "Site"
    
    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["title"] = self.title
        return ctx

URL configuration

path("posts/", PostListView.as_view(), name="list"),
path("posts/<slug:slug>/", PostDetailView.as_view(), name="detail"),
path("posts/new/", PostCreateView.as_view(), name="create"),
path("posts/<int:pk>/edit/", PostUpdateView.as_view(), name="edit"),
path("posts/<int:pk>/delete/", PostDeleteView.as_view(), name="delete"),

as_view() kwargs

path("posts/", PostListView.as_view(paginate_by=10, template_name="custom.html"))

View (base class)

from django.views import View

class MyView(View):
    def get(self, request):
        return HttpResponse("GET")
    
    def post(self, request):
        return HttpResponse("POST")

For when generic CBV is overkill, but you want method dispatch.

Method dispatching

def dispatch(self, request, *args, **kwargs):
    if request.user.is_blocked:
        return HttpResponseForbidden()
    return super().dispatch(request, *args, **kwargs)

FBV vs CBV

FBV wins for:

  • Simple custom logic.
  • Easier to read top-to-bottom.

CBV wins for:

  • CRUD patterns (use the generics).
  • Composition via mixins.
  • Reusable view logic.

Don’t religiously pick one — use what reads clearly.

get_object override

def get_object(self, queryset=None):
    obj = super().get_object(queryset)
    if obj.author != self.request.user:
        raise Http404
    return obj

form_invalid

def form_invalid(self, form):
    messages.error(self.request, "Fix the errors below")
    return super().form_invalid(form)

Common mistakes

  • Forgetting success_url — redirect fails.
  • Using model.objects.all() instead of get_queryset() for filtered list.
  • LoginRequiredMixin not first in MRO order — login_url ignored.
  • Two CBVs sharing a template without specifying template_name.
  • Forgetting to call super() in lifecycle methods.

Read this next

If you want my CBV mixins + base classes, they’re at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .