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-адресами.

Проверьте себя
1. Почему CBV подключают в urls.py через .as_view()?
AДля красоты
BURL-резолвер ждёт функцию, а as_view() возвращает функцию-обёртку над классом
CЭто ускоряет рендеринг
DЧтобы скрыть класс
2. Что делает ListView без дополнительного кода?
AУдаляет объекты
BДостаёт список объектов модели, добавляет пагинацию и передаёт в шаблон
CСоздаёт миграции
DНастраивает сервер