Class-Based Views и generic views
Class-Based Views (CBV) — это готовые шаблоны типовых views. Списки, детали, формы создания и редактирования Django умеет генерировать почти без кода.
Суть: CBV выражают паттерны через классы и наследование. Generic views (ListView, DetailView, CreateView) дают готовый CRUD, а в URL они подключаются через .as_view().
Зачем нужны классы вместо функций
90% веб-приложений делают одно и то же: показать список объектов, показать один объект, создать, отредактировать, удалить. Писать это функциями каждый раз — копипаста. Class-Based Views решают проблему через наследование: Django предоставляет готовые «дженерики», а вы лишь указываете модель и шаблон.
from django.views.generic import ListView, DetailView, CreateView
from .models import Post
class PostListView(ListView):
model = Post
template_name = "blog/post_list.html"
context_object_name = "posts"
paginate_by = 10
class PostDetailView(DetailView):
model = Post
template_name = "blog/post_detail.html"
Сравните: ListView сам достаёт все объекты, добавляет пагинацию, передаёт в шаблон — всё за пять строк, что функцией заняло бы вдвое больше.
Подключение через as_view()
В отличие от функций, классы нельзя передать в path() напрямую — у URL-резолвера должна быть функция. Поэтому CBV подключают через метод .as_view(), который и возвращает функцию-обёртку:
from django.urls import path
from . import views
urlpatterns = [
path("", views.PostListView.as_view(), name="post_list"),
path("<int:pk>/", views.PostDetailView.as_view(), name="post_detail"),
]
Generic views для всего CRUD
Полный набор дженериков покрывает CRUD: ListView (список), DetailView (один объект), CreateView (создание формой), UpdateView (редактирование), DeleteView (удаление). CreateView и UpdateView сами строят форму из модели, валидируют её и сохраняют — об этом подробнее в разделе про формы.
Как это работает под капотом
Метод .as_view() создаёт экземпляр класса и возвращает функцию, которая при запросе вызывает dispatch(). dispatch() смотрит на HTTP-метод и вызывает соответствующий метод объекта: get() для GET, post() для POST. Это та же диспетчеризация, что и в FBV, но оформленная через методы класса. Наследование и переопределение методов — языко-независимая механика, которую видно на чистом Python:
# Попробуй сам ▶ — dispatch и наследование как в CBV
class View:
def dispatch(self, method):
handler = getattr(self, method.lower(), self.not_allowed)
return handler()
def not_allowed(self):
return "405 Method Not Allowed"
class ListView(View):
model = None
def get(self):
return f"200: список объектов {self.model}"
class PostListView(ListView):
model = "Post"
class PostCreateView(ListView):
model = "Post"
def post(self): # добавили обработку POST
return f"302: создан {self.model}"
print(PostListView().dispatch("GET"))
print(PostCreateView().dispatch("GET"))
print(PostCreateView().dispatch("POST"))
print(PostListView().dispatch("DELETE"))
Django делает то же: dispatch находит метод по имени HTTP-глагола, а наследование позволяет переопределять только то, что нужно изменить.
FBV или CBV — что выбрать
Это не «или-или». FBV проще читать и отлаживать, они идеальны для нестандартной логики. CBV блистают на типовом CRUD, где экономят много кода и дают переиспользование через миксины. Опытные команды используют оба подхода в одном проекте: дженерики для рутины, функции для уникальных случаев.
Частые ошибки
- Забыть .as_view() в urls.py. Передадите класс вместо функции — ошибка.
- Переопределять get/post, не зная порядка вызова. Изучите цепочку
dispatch → get → get_context_data. - Тянуть CBV в нестандартную логику. Иногда простая функция понятнее, чем борьба с миксинами.
- Не задать context_object_name. Тогда в шаблоне переменная зовётся
object_list.
Best practices
- Используйте дженерики для типового CRUD, FBV — для уникальной логики.
- Переопределяйте узкие методы (
get_queryset,get_context_data), а не весьget. - Выносите общую логику в миксины (например,
LoginRequiredMixin). - Задавайте понятные
context_object_nameдля читаемых шаблонов.
Итоги
CBV выражают типовые паттерны через классы. Дженерики (ListView, DetailView, CreateView и др.) дают готовый CRUD за считанные строки. Подключаются через .as_view(), диспетчеризуют запросы методом dispatch. FBV и CBV дополняют друг друга. Теперь свяжем views с URL-адресами.