Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Представления: FBV и CBV
views_fbv_cbv

Представления: FBV и CBV

Function-based vs Class-based views, mixins, decorators

Представления Django: FBV и CBV

Представление принимает HttpRequest и возвращает HttpResponse либо совместимый объект ответа. Django предлагает две формы записи: функцию (function-based view, FBV) и класс (class-based view, CBV). Это не два разных уровня возможностей — выбор определяет структуру кода.

Материал актуален для Django 5.2 LTS и 6.0.

#Один контракт, две формы

Простейшая FBV выглядит так:

from django.http import HttpResponse from django.views.decorators.http import require_GET @require_GET def health(request): return HttpResponse("ok", content_type="text/plain")

Эквивалентная CBV наследуется от View и объявляет обработчик HTTP-метода:

from django.http import HttpResponse from django.views import View class HealthView(View): http_method_names = ["get", "head", "options"] def get(self, request): return HttpResponse("ok", content_type="text/plain")

В URLconf функция передаётся напрямую, а класс преобразуется в вызываемую view-функцию через as_view():

from django.urls import path urlpatterns = [ path("health/", health, name="health"), path("health-cbv/", HealthView.as_view(), name="health_cbv"), ]

as_view() создаёт новый экземпляр класса для каждого запроса, вызывает setup(), затем dispatch(). Настройки класса можно передать в as_view(), но это общая конфигурация, а не место для изменяемого состояния запроса.

#Когда функция делает код яснее

FBV хорошо подходит короткому сценарию, который читается сверху вниз. Например, форма обратной связи имеет две явные ветви:

from django.contrib import messages from django.shortcuts import redirect, render from django.views.decorators.http import require_http_methods from .forms import FeedbackForm @require_http_methods(["GET", "POST"]) def feedback(request): form = FeedbackForm(request.POST or None) if request.method == "POST" and form.is_valid(): form.save() messages.success(request, "Сообщение принято") return redirect("feedback_done") return render(request, "feedback/form.html", {"form": form})

Здесь форма отвечает за преобразование и валидацию ввода, именованный URL — за перенаправление, а схема POST/redirect/GET защищает от повторной отправки при обновлении страницы.

Не создавайте модель напрямую из request.POST.get() только потому, что пример должен быть коротким. Тогда исчезают типовая валидация, нормальные сообщения об ошибках и явный список разрешённых полей.

#Когда класс уменьшает повторение

CBV удобна, если поведение естественно разделяется по HTTP-методам или собирается из стандартных mixin. Та же форма на базовом View:

from django.contrib import messages from django.shortcuts import redirect, render from django.views import View from .forms import FeedbackForm class FeedbackView(View): template_name = "feedback/form.html" def get(self, request): return render(request, self.template_name, {"form": FeedbackForm()}) def post(self, request): form = FeedbackForm(request.POST) if form.is_valid(): form.save() messages.success(request, "Сообщение принято") return redirect("feedback_done") return render(request, self.template_name, {"form": form})

Этот вариант длиннее, поэтому для одного простого сценария CBV не даёт автоматического выигрыша. Его преимущество проявится, если проект уже использует общие mixin или generic views.

#Что делает dispatch()

dispatch() смотрит на request.method и вызывает get(), post(), put() и т. д. Если метод не разрешён или обработчик отсутствует, базовый View возвращает 405 Method Not Allowed и корректный заголовок Allow.

Если head() не объявлен, базовый View использует get() как обработчик HEAD, а сервер не отправляет тело ответа. Не задавайте выдуманный Content-Length: он должен соответствовать реальному представлению ресурса.

OPTIONS также обрабатывается базовым классом. Атрибут http_method_names позволяет сузить поддерживаемый набор, но безопасность записи всё равно требует авторизации, CSRF-защиты и валидации данных.

request.POST заполняется для отправленных form-данных, но не является универсальным парсером JSON и PUT. Для полноценного API удобнее Django REST Framework; ручной endpoint должен явно разобрать content type, ограничить размер тела, обработать ошибочный JSON и вернуть корректные статусы.

#Переопределение dispatch()

Общую логику можно поставить до и после обработчика:

import logging from time import monotonic from django.views import View logger = logging.getLogger(__name__) class TimedView(View): def dispatch(self, request, *args, **kwargs): started_at = monotonic() try: return super().dispatch(request, *args, **kwargs) finally: logger.info( "view_finished", extra={ "method": request.method, "path": request.path, "duration_seconds": monotonic() - started_at, }, )

Ключевое слово — super(): без него цепочка наследования не дойдёт до следующего mixin и обработчика метода. Для авторизации лучше взять готовые mixin Django, а для сквозного поведения всего проекта — рассмотреть middleware. Логирование в каждом классе через собственный mixin быстро превращается в дублирование.

#Mixin и порядок MRO

Mixin добавляет узкую часть поведения. Встроенные auth-mixin располагают слева от самой view:

from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from django.views.generic import UpdateView class ArticleUpdateView( LoginRequiredMixin, PermissionRequiredMixin, UpdateView, ): permission_required = "articles.change_article" raise_exception = True model = Article form_class = ArticleForm template_name = "articles/article_form.html" def get_queryset(self): queryset = super().get_queryset() if self.request.user.is_staff: return queryset return queryset.filter(author=self.request.user)

Проверка права модели и ограничение доступных объектов решают разные задачи. Одного change_article недостаточно, если обычный автор должен редактировать только свои статьи. Ограничение в get_queryset() также не раскрывает существование чужого объекта: для него получится 404.

Python строит MRO слева направо по алгоритму C3. Кооперативный mixin принимает *args, **kwargs и вызывает super(). Если один компонент оборвёт цепочку, mixin справа может не выполниться. При неясности посмотрите ArticleUpdateView.__mro__ и исходный код generic view.

#Декораторы для CBV

Функциональный декоратор адаптируют через method_decorator:

from django.utils.decorators import method_decorator from django.views.decorators.cache import never_cache @method_decorator(never_cache, name="dispatch") class PrivateDashboardView(LoginRequiredMixin, View): def get(self, request): return render(request, "dashboard/private.html")

Декорирование dispatch охватывает все HTTP-методы. Декорирование только get не защитит post. Порядок нескольких декораторов влияет на порядок входа и выхода, поэтому длинную цепочку лучше заменить понятным mixin или вынести сквозную политику в middleware.

#JSON-ответ без самодельного mixin

Для небольшого служебного endpoint достаточно JsonResponse:

from django.http import JsonResponse from django.views import View class ArticleSummaryView(View): def get(self, request, pk): article = get_object_or_404( Article.objects.filter(status=Article.Status.PUBLISHED), pk=pk, ) return JsonResponse({ "id": article.pk, "title": article.title, })

Фильтр опубликованных статей применяется и к списку, и к детали; иначе URL детали может раскрыть черновик. JsonResponse сам сериализует словарь и выставляет content type. Авторизация и владение должны проверяться на каждом изменяющем endpoint, а не только в интерфейсе.

#FBV или CBV: практическое правило

Выберите форму, в которой политика приложения видна без прыжков по пяти родительским классам:

  • короткая уникальная ветвящаяся операция часто яснее как FBV;
  • стандартные списки, детали и формы обычно проще на generic CBV;
  • повторяемая проверка доступа подходит готовому mixin;
  • общая обработка всех запросов проекта относится к middleware;
  • сложную бизнес-операцию не следует прятать ни во view, ни в mixin — вынесите её в сервисную функцию и вызовите из тонкого представления.

Это рекомендации, а не запреты. Переписывание рабочей FBV в CBV само по себе не улучшает архитектуру.

#Legacy и актуальность

FBV, View, as_view(), dispatch() и generic CBV остаются актуальными в Django 5.2/6.0. Legacy-проекты часто содержат собственные JSON-mixin, ручные проверки request.user и жёстко заданные URL. Их не нужно механически удалять: сначала добавьте тесты, затем заменяйте на JsonResponse, auth-mixin и reverse()/redirect() с именованными маршрутами.

Самодельная CBV, где post() заканчивается pass, не является заготовкой безопасного API: обработчик вернёт None, и Django завершит запрос ошибкой. Даже учебный листинг должен либо возвращать ответ, либо явно быть помечен как неполный псевдокод.

#Документация

  • Обзор class-based views
  • Базовый View
  • Auth mixins
  • Декорирование CBV

Далее: Generic Class-Based Views