В предыдущем уроке мы подробно разобрали FormView — универсальный инструмент для отображения и обработки HTML-форм. Однако в реальных проектах Django большинство форм напрямую связано с моделями базы данных: мы либо создаём новый объект, либо редактируем существующий.
Для этих двух сценариев Django предоставляет специализированные классы:
- CreateView — для создания новых объектов;
- UpdateView — для редактирования уже существующих записей.
Они построены поверх FormView, но идут дальше:
Django автоматически связывает форму с моделью, управляет сохранением объекта и заполняет форму данными из базы.
В этом уроке мы разберём:
- чем
CreateViewотличается отFormView; - когда использовать форму, а когда —
fields; - как работает
UpdateView; - как выбирать объект для редактирования по
pkиslug; - какие ошибки встречаются чаще всего и почему они возникают.
Все примеры будут построены на сущностях проекта cinemahub, в первую очередь — на модели Movie.
Оба класса решают одну и ту же задачу — работу с моделью через форму, но в разных режимах:
| Класс | Назначение |
|---|---|
| CreateView | Создание новой записи |
| UpdateView | Редактирование существующей записи |
Они автоматически:
- создают форму на основе модели;
- валидируют данные;
- сохраняют объект в базе;
- выполняют редирект после успеха;
- передают форму в шаблон под именем
form.
По сути, CreateView и UpdateView — это FormView + ModelForm + логика сохранения, упакованные в один класс.
Начнём с самого распространённого сценария — добавления нового фильма в каталог.
Предположим, у нас уже есть форма MovieCreateForm, созданная в предыдущем уроке.
Файл: movies/views.py
from django.views.generic import CreateView
from django.urls import reverse_lazy
from .models import Movie
from .forms import MovieCreateFormСоздадим класс-представление:
class MovieCreateView(CreateView):
form_class = MovieCreateForm
template_name = 'movies/movie_form.html'
success_url = reverse_lazy('movies:list')
extra_context = {
'title': 'Добавление фильма'
}-
CreateViewсам:- создаёт экземпляр формы;
- проверяет данные;
- сохраняет объект
Movie;
-
form_classуказывает, какую форму использовать; -
template_name— шаблон для отображения; -
success_url— куда перенаправлять после успеха; -
extra_context— дополнительные данные в шаблон.
- Запускаем сервер.
- Переходим в браузере на
/movies/add/. - Видим форму добавления фильма.
- Заполняем данные и отправляем.
- Новый фильм появляется в базе, происходит редирект.
Если результат именно такой — CreateView работает корректно.
Иногда отдельная форма не нужна. Django позволяет создать форму на лету, прямо на основе модели.
Файл: movies/views.py
class MovieCreateView(CreateView):
model = Movie
template_name = 'movies/movie_form.html'
success_url = reverse_lazy('movies:list')
fields = ['title', 'description', 'year', 'is_published']
extra_context = {
'title': 'Добавление фильма'
}В этом случае Django:
- автоматически создаёт
ModelForm; - включает в форму только указанные поля;
- сам выполняет
save().
- форма простая;
- нет дополнительной логики;
- не требуется кастомная валидация.
- нужна сложная валидация;
- требуется переопределять
clean_*; - используется
commit=False; - форма применяется в нескольких местах.
Если в модели есть обязательное поле, а вы забыли указать его в fields, при отправке формы произойдёт ошибка.
Пример проблемы:
fields = ['title', 'year']Если description обязательное — форма будет невалидной.
Вывод: CreateView не отменяет требований модели. Все ограничения модели продолжают действовать.
Теперь перейдём ко второй ключевой задаче — редактированию существующего фильма.
Файл: movies/views.py
from django.views.generic import UpdateView
class MovieUpdateView(UpdateView):
model = Movie
fields = ['title', 'description', 'year', 'is_published']
template_name = 'movies/movie_form.html'
success_url = reverse_lazy('movies:list')
extra_context = {
'title': 'Редактирование фильма'
}UpdateView:
- находит объект в базе;
- подставляет его данные в форму;
- сохраняет изменения после отправки.
По умолчанию UpdateView ищет объект по pk.
Файл: movies/urls.py
path('edit/<int:pk>/', MovieUpdateView.as_view(), name='edit'),Переход по адресу:
/movies/edit/1/
Откроет форму редактирования фильма с id=1.
Для проектов вроде cinemahub это более естественный вариант.
Маршрут:
path('edit/<slug:slug>/', MovieUpdateView.as_view(), name='edit'),Если поле в модели называется slug, Django использует его автоматически.
Если имя другое — можно явно указать:
slug_field = 'slug'
slug_url_kwarg = 'slug'- Переходим по URL редактирования.
- Форма уже заполнена данными фильма.
- Меняем значения.
- Отправляем форму.
- Изменения сохраняются в базе.
Если форма пустая — ошибка в маршруте или параметрах поиска объекта.
Большое преимущество этих классов — один шаблон для создания и редактирования.
Пример: movies/templates/movies/movie_form.html
{% extends 'base.html' %} {% block content %}
<h1>{{ title }}</h1>
<form method="post">
{% csrf_token %} {{ form.as_p }}
<button type="submit">Сохранить</button>
</form>
{% endblock %}Шаблон не знает, создаём мы объект или редактируем — логика полностью в представлении.
Причина: объект с таким pk или slug не существует. Решение: проверить данные в базе.
Причина: неверное имя параметра в URL.
Решение: pk, slug_url_kwarg должны совпадать.
Причина: обязательное поле отсутствует в fields.
Решение: проверить модель.
Причина: ошибка в success_url.
Решение: проверить reverse_lazy() и namespace.
Создайте CreateView для добавления жанра (Genre) без отдельной формы, используя fields.
Поля:
- name
- slug
Создайте UpdateView для редактирования режиссёра (Director) по pk.
Создайте UpdateView для редактирования фильма по slug.
class GenreCreateView(CreateView):
model = Genre
fields = ['name', 'slug']
template_name = 'genres/genre_form.html'
success_url = reverse_lazy('genres:list')class DirectorUpdateView(UpdateView):
model = Director
fields = ['name', 'slug']
template_name = 'directors/director_form.html'
success_url = reverse_lazy('directors:list')class MovieUpdateView(UpdateView):
model = Movie
fields = ['title', 'description', 'year', 'is_published']
template_name = 'movies/movie_form.html'
success_url = reverse_lazy('movies:list')Маршрут:
path('edit/<slug:slug>/', MovieUpdateView.as_view(), name='edit'),- В чём основное отличие
CreateViewотFormView? - Когда лучше использовать
fields, а когдаform_class? - Как
UpdateViewнаходит объект для редактирования? - Что произойдёт, если обязательное поле не указано в
fields? - Можно ли использовать один шаблон для CreateView и UpdateView?
- Как изменить способ поиска объекта (pk → slug)?
- Где происходит сохранение данных в CreateView?
- Какой метод отвечает за редирект после успеха?