Django Class-Based Views для CRUD: шаг за шагом в менеджере задач

Что такое class-based views в Django
В Django представления (views) — это точки входа, которые принимают HTTP-запрос и возвращают HTTP-ответ. Class-based views (CBV) — это способ описывать представления классами вместо функций.
Преимущество CBV: лучше структурированный код, возможность наследования, переиспользование и встроенные методы для GET/POST и других операций. CBV упрощают создание стандартных CRUD-операций благодаря набору готовых классов (ListView, DetailView, CreateView, UpdateView, DeleteView и др.).
Определение в одну строку: CBV — это Python-классы, которые инкапсулируют логику обработки запросов и предоставляют переопределяемые методы для работы с формами, контекстом и перенаправлениями.
Важно: CBV не заменяют function-based views (FBV). Они дополняют инструментарий. Иногда FBV проще и прозрачнее для небольших эндпоинтов.
Встроенные class-based views в Django
Django содержит готовые CBV для типичных сценариев:
- ListView — рендерит список объектов из модели.
- DetailView — рендерит детальную страницу одного объекта.
- CreateView — отображает форму и обрабатывает создание нового объекта.
- UpdateView — отображает форму и обрабатывает обновление существующего объекта.
- DeleteView — показывает страницу подтверждения и удаляет объект.
- TemplateView — рендерит статичный шаблон с контекстом.
- RedirectView — делает перенаправления.
- FormView — упрощает обработку форм без прямой модели.
Каждый из этих классов можно конфигурировать через атрибуты (model, template_name, form_class, success_url и т. п.) и расширять через методы (get_queryset, get_context_data, form_valid и др.).
Когда CBV — хорошее решение и когда нет
Используйте CBV, если:
- Вам нужны стандартные CRUD-интерфейсы.
- Вы планируете реиспользовать логику через наследование.
- Нужна аккуратная организация кода при росте приложения.
Выбирайте FBV, если:
- Представление очень простое и одноразовое.
- Нужна детальная логика, плохо укладывающаяся в шаблон CBV.
Контрпример: если вам надо написать одну простую страницу API-эндпоинта, которая возвращает JSON и содержит три строки кода, FBV будет короче и читабельнее.
Что мы построим
Приложение: менеджер задач (Task Manager). Функциональность:
- список задач (Read list),
- страница задачи (Read detail),
- создание задачи (Create),
- редактирование задачи (Update),
- удаление задачи (Delete).
Дополнительно: в статье — чек-лист для ревьюера, критерии приёмки, тесты, мини-SOP по добавлению нового представления и decision tree для выбора между CBV и FBV.
Подготовка проекта
Шаги для создания проекта и приложения (дополненные инструкции и пояснения):
- Создайте виртуальное окружение и активируйте его. Для Unix/macOS:
python3 -m venv .venv
source .venv/bin/activate- Установите Django:
pip install django- Создайте проект project_core:
django-admin startproject project_core .- Создайте приложение task_manager:
python manage.py startapp task_manager- В settings.py добавьте ‘task_manager’ в INSTALLED_APPS:
INSTALLED_APPS = [
# другие приложения
'task_manager',
]- Настройте корневые URLы project_core/urls.py, подключив маршруты приложения:
from django.urls import path, include
urlpatterns = [
path('', include('task_manager.urls')),
]Примечание: держите статические файлы и шаблоны настроенными для вашего окружения. Для быстрого старта используйте DEBUG=True и встроенный сервер разработки.
Модель Task
Создайте модель задачи в task_manager/models.py. Простая и понятная модель подходит для демонстрации CRUD:
from django.db import models
class Task(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
completed = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.titleПримените миграции:
python manage.py makemigrations && python manage.py migrateФакт-бокс: модель содержит 4 поля: title, description, completed, created_at. created_at автоматически устанавливается при создании.
Форма для Create и Update
Создайте task_manager/forms.py:
from django import forms
from .models import Task
class TaskForm(forms.ModelForm):
class Meta:
model = Task
fields = ['title', 'description', 'completed']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'description': forms.Textarea(attrs={'class': 'form-control'}),
'completed': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
}Пояснение: widgets помогают интегрировать фронтенд-стили (например, Bootstrap) без изменения логики формы.
Представления на основе классов
Откройте task_manager/views.py и добавьте импорты и реализации CBV. Ниже — полный пример с комментариями.
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Task
from .forms import TaskForm
class TaskListView(ListView):
model = Task
template_name = 'task_manager/task_list.html'
context_object_name = 'tasks'
paginate_by = 10 # добавили пагинацию по умолчанию
class TaskDetailView(DetailView):
model = Task
template_name = 'task_manager/task_detail.html'
class TaskCreateView(CreateView):
model = Task
form_class = TaskForm
template_name = 'task_manager/task_form.html'
success_url = reverse_lazy('task_list')
class TaskUpdateView(UpdateView):
model = Task
form_class = TaskForm
template_name = 'task_manager/task_form.html'
success_url = reverse_lazy('task_list')
class TaskDeleteView(DeleteView):
model = Task
template_name = 'task_manager/task_confirm_delete.html'
success_url = reverse_lazy('task_list')Пояснения по коду:
- paginate_by в ListView улучшает UX при большом количестве задач.
- reverse_lazy используется, потому что URL-конфигурация еще не загружена на момент импорта.
Маршруты (URLs)
Создайте task_manager/urls.py и опишите маршруты:
from django.urls import path
from .views import TaskListView, TaskDetailView, TaskCreateView, TaskUpdateView, TaskDeleteView
urlpatterns = [
path('', TaskListView.as_view(), name='task_list'),
path('create/', TaskCreateView.as_view(), name='task_create'),
path('tasks//', TaskDetailView.as_view(), name='task_detail'),
path('tasks//update/', TaskUpdateView.as_view(), name='task_update'),
path('tasks//delete/', TaskDeleteView.as_view(), name='task_delete'),
] Совет: для человеко-понятных URL можно заменить
Шаблоны
Создайте шаблоны в task_manager/templates/task_manager/. Ниже — базовые примеры. Оставляем расширение base.html на ваше усмотрение.
task_list.html
{% extends 'base.html' %}
{% block content %}
Your Tasks
Add Task
{% for task in tasks %}
{{ task.title }}
{{ task.description|truncatechars:50 }}
Completed:
{% if task.completed %}Yes{% else %}No{% endif %}
Read more
Delete task
{% empty %}
No tasks yet.
Add Task
{% endfor %}
{% endblock %}task_detail.html
{% extends 'base.html' %}
{% block content %}
{{ task.title }}
{{ task.description }}
Completed: {% if task.completed %}Yes{% else %}No{% endif %}
Edit task
Delete task
{% endblock %}task_form.html
{% extends 'base.html' %}
{% block content %}
Create Task
{% endblock %}task_confirm_delete.html
{% extends 'base.html' %}
{% block content %}
Confirm Delete
Are you sure you want to delete "{{ object.title }}"?
{% endblock %}Совет по UX: добавьте предупреждение на JavaScript и undo через soft-delete для важных данных.
Расширение: добавление поиска и фильтрации
Чтобы позволить пользователю искать задачи, расширьте TaskListView:
class TaskListView(ListView):
model = Task
template_name = 'task_manager/task_list.html'
context_object_name = 'tasks'
paginate_by = 10
def get_queryset(self):
qs = super().get_queryset().order_by('-created_at')
q = self.request.GET.get('q')
if q:
qs = qs.filter(title__icontains=q) | qs.filter(description__icontains=q)
return qsВ шаблоне добавьте форму поиска:
Тесты и критерии приёмки
Критерии приёмки:
- Страница списка возвращает код 200 и содержит список задач.
- Создание задачи через CreateView создаёт запись в базе и редиректит на список.
- UpdateView корректно меняет поля модели.
- DeleteView удаляет задачу и перенаправляет на список.
Пример простого теста в task_manager/tests.py:
from django.test import TestCase
from django.urls import reverse
from .models import Task
class TaskViewsTest(TestCase):
def test_create_and_list(self):
resp = self.client.post(reverse('task_create'), {'title': 't1', 'description': 'd', 'completed': False})
self.assertEqual(resp.status_code, 302) # redirect
resp = self.client.get(reverse('task_list'))
self.assertContains(resp, 't1')Мини-SOP: добавить новое представление на основе CBV
- Добавьте/обновите модель в models.py.
- Создайте миграцию и примените её.
- При необходимости добавьте форму в forms.py.
- Добавьте класс-представление в views.py, наследуя подходящий CBV.
- Добавьте маршрут в urls.py и имя URL.
- Создайте шаблон, использующий context_object_name или object.
- Напишите тесты и запустите их.
- Обновите документацию и CHANGELOG.
Decision tree: выбрать CBV или FBV
flowchart TD
A[Нужно CRUD?] -->|Да| B[Использовать CBV]
A -->|Нет| C[Небольшой эндпоинт]
C --> D[Использовать FBV]
B --> E[Нужна тонкая логика?]
E -->|Да| F[Можно комбинировать: CBV с переопределением методов]
E -->|Нет| G[Стандартная CBV]Роли и чек-листы
Разработчик:
- Модель соответствует требованиям.
- Формы валидируют поля.
- URLы имеют имена.
- Тесты покрывают основные сценарии.
Код-ревьюер:
- Представления читаемы и понятны.
- Не дублируется логика (вынесена в mixin/utility).
- Нет прямых SQL-запросов без причин.
DevOps/Инженер развёртывания:
- Настроены статики и шаблоны для production.
- Миграции в порядке и совместимы с резервным копированием БД.
Альтернативы и интеграции
- FBV — проще для единичных маршрутов.
- Django REST Framework — если нужен API; там используют ViewSet и GenericAPIView.
- Использовать mixins и собственные базовые классы для общих сценариев (например, AuditMixin для логирования изменений).
Тонкости и возможные ошибки
- reverse_lazy vs reverse: при импортах модулей используйте reverse_lazy в классовых переменных.
- context_object_name: если не задано, ListView отдаёт объект под именем object_list.
- в UpdateView и CreateView при form_valid можно дописать дополнительную логику (например, связывание с пользователем).
Пример переопределения form_valid:
class TaskCreateView(CreateView):
# ...
def form_valid(self, form):
# можно установить автора: form.instance.owner = self.request.user
return super().form_valid(form)Тест-кейсы для приёмки
- Создание задачи с пустым title должно выдавать ошибку валидации.
- Попытка обновить несуществующую задачу должна вернуть 404.
- Удаление задачи должно уменьшать счётчик объектов в базе.
- Пагинация должна корректно отображать страницы при > paginate_by задач.
Безопасность и приватность
- Используйте CSRF-токены в формах (Django делает это стандартно в шаблонах).
- Ограничивайте доступ к CRUD-операциям через декораторы или mixin-ы (LoginRequiredMixin, PermissionRequiredMixin).
- Для многопользовательских приложений проверяйте, что пользователь может изменять только свои задачи.
Пример ограничения доступа:
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
class TaskUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
# ...
def test_func(self):
return self.get_object().owner == self.request.userСценарии отказа и как их обрабатывать
- Сбой БД: возвращать 5xx и показывать страницу с сообщением об ошибке.
- Массовые операции удаления: использовать подтверждение и/или soft-delete.
- Конфликт одновременных обновлений: внедрить optimistic locking (версия) или блокировки на уровне транзакции.
Итог и рекомендации
Class-based views отлично подходят для типичных CRUD-сценариев. Они ускоряют разработку, улучшают структуру кода и дают мощные точки расширения через наследование и mixin-ы. Используйте CBV для повторяемых шаблонов, и не стесняйтесь переключаться на FBV, если задача требует простой и прозрачной логики.
Важно: пишите тесты и определяйте критерии приёмки перед реализацией. Это уменьшит количество регрессий и ускорит код-ревью.
Summary:
- CBV экономят время при создании CRUD-интерфейсов.
- Для сложной логики комбинируйте CBV с переопределением методов.
- Всегда добавляйте проверки доступа и тесты.
Important: перед деплоем проверьте настройки STATIC_ROOT, ALLOWED_HOSTS и конфигурацию базы данных.
Краткая шпаргалка по атрибутам CBV
- model — модель, с которой работает представление.
- template_name — шаблон для рендера.
- context_object_name — имя переменной в контексте шаблона.
- form_class — класс формы для Create/Update.
- success_url — адрес для перенаправления после успешной операции.
- paginate_by — размер страницы для ListView.
Конец статьи.
Похожие материалы
Kanban в Obsidian — руководство и шаблоны
Crunchyroll на Nintendo Switch — как скачать
Как заполнить бесплатный OneDrive — идеи и шаблоны
Бейдж Facebook в подписи Mail на Mac
Удалить OneDrive в Windows 10 и 8.1 — полная инструкция