Class-Based Views
After a few weeks, the cookbook's views.py shows a pattern: list some objects, show one object, handle a form. Every list view looks the same apart from the model. Class-based views let Django own that repeated shape, so your code states only what is different.
The rule
A class per view, a method per verb; generic views fill in the rest.
The base View class sends each request to the method named after its HTTP verb:
from django.shortcuts import render
from django.views import View
class RecipeSearchView(View):
def get(self, request):
return render(request, "recipes/search.html")
def post(self, request):
... # handle the submitted searchIf a verb has no method, say a DELETE, Django answers 405 Method Not Allowed for you. No if request.method == ... branches.
URL patterns need a function, so every class-based view is wired in with .as_view():
path("search/", views.RecipeSearchView.as_view(), name="search"),The real payoff is generic views, ready-made classes for common jobs:
from django.views.generic import DetailView, ListView
from .models import Recipe
class RecipeListView(ListView):
model = Recipe
ordering = ["-created"]
class RecipeDetailView(DetailView):
model = Recipepath("", views.RecipeListView.as_view(), name="list"),
path("<slug:slug>/", views.RecipeDetailView.as_view(), name="detail"),Those few lines replace both function views. From model = Recipe, Django works out the rest by convention:
ListViewrendersrecipes/recipe_list.htmlwithobject_list(andrecipe_list) in the context.DetailViewreadsslug(orpk) from the URL, finds that recipe, raises 404 if none exists, and rendersrecipes/recipe_detail.htmlwithobject(andrecipe).
To change behaviour, override an attribute or method: template_name, paginate_by = 10, or get_queryset() to filter.
Function views are not obsolete. A one-off page with unusual logic is often clearer as a function. Use classes when the job matches a pattern Django already knows.
The shortcut
Ask "which generic view already does this?" List of things → ListView. One thing → DetailView. Create, edit, delete → CreateView, UpdateView, DeleteView. Then set attributes until it fits.
Pick the generic
It is a list, so
ListView.
The classic slip is writing views.RecipeListView in urls.py without .as_view(); the class itself is not a view function, and the page breaks.
Drills
Answer fast — trust the rule.
How do you use RecipeListView in a URL pattern?
A View subclass defines only get(). What does a POST request receive?
Which template does DetailView with model = Recipe look for by default?
A ListView should show only recipes under 30 minutes. What do you override?
Remember
- A class per view, a method per verb; generic views fill in the rest.
- Ask "which generic view already does this?", then override only what differs.
- Trap: forgetting
.as_view()inurls.py.
