Рефакторинг представления на основе классов (CBV)
В этой главе вы изучите представления Django на основе классов (CBV) и отрефакторите главную страницу и страницу с подробностями, используя более лаконичный код.
FBV против CBV
До сих пор все наши представления были написаны с использованием функций (представления на основе функций, FBV).
Django также предоставляетпредставление на основе классов (CBV), используя классы для инкапсуляции логики представления.
| Особенности | FBV (представление на основе функций) | CBV (представления на основе классов) |
|---|---|---|
| объём кода | Гибко, но общую логику приходится писать повторно | После наследования обобщённого представления требуется всего несколько строк кода |
| Читаемость | Линейный поток, понятная логика | Цепочку наследования нужно отслеживать, но, освоившись, всё ясно с первого взгляда. |
| Переиспользуемость | Инкапсулируется с помощью функций | Использование композиции Mixin (визитная карточка Django) |
| Применимые сценарии | Страницы с простой логикой и понятным потоком | Типовые страницы, такие как создание, чтение, обновление, удаление (CRUD) |
| Диспетчеризация HTTP-методов | Вручную: if request.method == 'POST' | Автоматическая диспетчеризация в методы get() / post() |
Между ними нет абсолютного преимущества или недостатка; в проектах Django обычно...Смешивание FBV и CBV: используйте FBV для сложной логики, а CBV — для стандартного CRUD.
ListView — рефакторинг страницы списка
Index-представление домашней страницы по сути:Запросить список статей → отрендерить шаблон。
Для такой шаблонной логики Django...ListViewУже написано для вас.
Пример
from django.views.generic import ListView
from django.db.models import Q
from .models import Post, Category
class PostListView(ListView):
"""Домашняя страница блога: наследует ListView, отображает список статей"""
model = Post # Укажите модель
template_name = 'blog/index.html' # Укажите шаблон (по умолчанию: blog/post_list.html)
context_object_name = 'posts' # Имя переменной, используемой в шаблоне (по умолчанию: object_list)
paginate_by = 12 # 12 элементов на странице (пагинация)
ordering = ['-created_at'] # Сортировка
def get_queryset(self):
"""Переопределение метода запроса: поддерживает фильтрацию по категориям + поиск по ключевым словам"""
queryset = super().get_queryset()
# Фильтр по категории
self.category_slug = self.request.GET.get('category', '')
if self.category_slug:
queryset = queryset.filter(category__slug=self.category_slug)
# Поиск по ключевым словам
self.keyword = self.request.GET.get('q', '')
if self.keyword:
queryset = queryset.filter(
Q(title__icontains=self.keyword) |
Q(summary__icontains=self.keyword)
)
return queryset
def get_context_data(self, **kwargs):
"""Переопределение метода контекста: внедрение дополнительных данных в шаблон"""
context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all()
context['category_slug'] = self.category_slug
context['keyword'] = self.keyword
context['title'] = 'EXAMPLE Blog - Home'
return context
Сравните объём кода до и после рефакторинга: FBV — около 40 строк → CBV — около 30 строк, а логика чётко разделена на разные методы.
DetailView — рефакторинг детальной страницы
Пример
from django.views.generic import DetailView
class PostDetailView(DetailView):
"""Страница подробностей статьи: наследует DetailView"""
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
# pk_url_kwarg: имя параметра для передачи первичного ключа в URL (по умолчанию — pk; здесь объявлено явно).
pk_url_kwarg = 'pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = f'{self.object.title} - ПРИМЕР Блог'
return context
Обновление конфигурации маршрута
Синтаксис маршрутизации CBV немного отличается от FBV — требуется вызов....as_view()。
Пример
from django.urls import path
from django.contrib.auth import views as auth_views
from . import views
urlpatterns = [
# CBV: добавьте .as_view() после класса, чтобы преобразовать его в функцию представления
path('', views.PostListView.as_view(), name='index'),
path('post/<int:pk>/', views.PostDetailView.as_view(), name='post_detail'),
# Остальные представления остаются без изменений
path('register/', views.register, name='register'),
path('login/', auth_views.LoginView.as_view(
template_name='blog/login.html',
redirect_authenticated_user=True
), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('post/<int:pk>/favorite/', views.toggle_favorite, name='toggle_favorite'),
]
Зачем использовать CBV?
.as_view()? Конфигурация URL Django ожидает вызываемый объект (функцию)..as_view()Он преобразует классовое представление в вызываемый объект, соответствующий сигнатуре функции представления Django, и внутри автоматически выполняет диспетчеризацию к методу get() или post() класса в зависимости от HTTP-метода (GET/POST).
Обзор распространённых обобщённых представлений
| представление | Имя шаблона по умолчанию | Назначение |
|---|---|---|
| ListView | modelname_list.html | Отобразить список объектов |
| DetailView | modelname_detail.html | Отобразить сведения об одном объекте |
| CreateView | modelname_form.html | Создать новый объект (с помощью формы) |
| UpdateView | modelname_form.html | Обновить существующий объект |
| DeleteView | model_name_confirm_delete.html | Подтвердить удаление объекта |
| TemplateView | нужно указать | Отрендерить статический шаблон (без запроса данных) |
Обобщённые представления Django покрывают около 80% сценариев веб-разработки. Если вы столкнулись с ситуациями, которые не отвечают вашим потребностям, вернитесь к FBV.
LoginRequiredMixin
CBV недоступен@login_requiredДекораторы (они предназначены для функций), переключитесь на...LoginRequiredMixin。
Пример
from django.views.generic import ListView
class FavoriteListView(LoginRequiredMixin, ListView):
"""Список избранного: для доступа требуется вход"""
model = Post
template_name = 'blog/favorites.html'
context_object_name = 'posts'
ordering = ['-created_at']
# LoginRequiredMixin: неаутентифицированные пользователи будут автоматически перенаправлены на settings.LOGIN_URL
def get_queryset(self):
# Показывать только статьи, добавленные в избранное текущим пользователем
return self.request.user.favorite_posts.all().order_by('-created_at')
Порядок наследования миксинов очень важен:
LoginRequiredMixinДолжен быть указан вListViewРанее. Порядок разрешения множественного наследования в Python — слева направо; LoginRequiredMixin сначала проверяет статус входа, и только после успешной проверки продолжает выполнение логики ListView.
Когда использовать CBV, а когда FBV?
Практическое правило:
- FBV: представления со сложной логикой, множеством условных ветвлений и нестандартными сценариями (например, toggle_favorite)
- CBV: Представления со стандартными CRUD-операциями и логикой, которую можно чётко разделить на get/post (например, страницы списков, страницы деталей)
Операции вроде toggle_favorite (переключение избранного) имеют простую логику, но поток, отличный от обычного CRUD; FBV подходит больше.
Для стандартных страниц отображения данных, таких как index и post_detail, CBV может значительно сократить шаблонный код.
Итоги главы
В этой главе вы узнали об основных способах использования классовых представлений Django: ListView отображает списки, DetailView отображает детали, get_queryset/get_context_data настраивают запросы и контекст, as_view() обеспечивает маршрутизацию, а LoginRequiredMixin заменяет login_required.
Код переработанной главной страницы и страницы деталей стал короче и имеет более ясную структуру.
Другие расширения