На предыдущих уроках мы научились:
- выводить список записей в админ-панели,
- управлять отображением колонок,
- настраивать поиск и фильтры,
- добавлять собственные фильтры.
Теперь мы переходим к очень важной части работы администратора — настройке формы редактирования записи. Именно эта форма открывается, когда администратор нажимает «Добавить» или «Изменить» запись в админ-панели.
Сегодня мы:
- разберём, как управлять полями формы,
- научимся скрывать поля,
- делать их только для чтения,
- автоматически заполнять
slug, - улучшать работу с полями Many-to-Many,
- обработаем типичные ошибки и посмотрим, как их исправлять.
Перед началом работы напомним структуру данных, которая используется в этом модуле курса.
Файлы находятся в приложении movies.
from django.db import models
from django.utils.text import slugify
class Genre(models.Model):
name = models.CharField(max_length=100, unique=True)
def __str__(self):
return self.name
class Actor(models.Model):
name = models.CharField(max_length=120)
slug = models.SlugField(unique=True)
bio = models.TextField(blank=True)
def __str__(self):
return self.name
class Movie(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
description = models.TextField()
year = models.PositiveIntegerField()
genre = models.ForeignKey(Genre, on_delete=models.PROTECT)
actors = models.ManyToManyField(Actor, blank=True)
is_published = models.BooleanField(default=True)
def __str__(self):
return self.titleТеперь мы будем настраивать админ-панель, опираясь именно на эту структуру.
По умолчанию Django:
- показывает все редактируемые поля модели,
- упорядочивает их в том порядке, в котором они объявлены в модели,
- делает все поля доступными для редактирования,
- отображает поля Many-to-Many в виде списка, где нужно зажимать Ctrl.
Мы можем изменить любое поведение с помощью параметров в классе ModelAdmin.
Для этого создаём файл:
movies/admin.py
Там будем указывать настройки.
Иногда администратору не нужны все поля модели.
Например, мы можем временно скрыть is_published, чтобы администратор не менял публикацию, или показывать поля в другом порядке.
movies/admin.py:
from django.contrib import admin
from .models import Movie, Genre, Actor
@admin.register(Movie)
class MovieAdmin(admin.ModelAdmin):
fields = ['title', 'slug', 'description', 'year', 'genre']Теперь при открытии фильма мы увидим только эти поля и именно в таком порядке.
- Перейдите в админ-панель
http://127.0.0.1:8000/admin/movies/movie/ - Откройте любой фильм или создайте новый.
- Убедитесь, что поля
actorsиis_publishedисчезли.
Предположим, мы попытались создать новый фильм, но не включили в форму обязательное поле genre:
fields = ['title', 'slug', 'description', 'year']При сохранении появится ошибка:
This field is required.
Django сообщает: поле genre обязательно, но его нет в форме.
Просто добавляем поле:
fields = ['title', 'slug', 'description', 'year', 'genre']Теперь запись сохраняется корректно.
Иногда удобнее убрать ненужные поля, чем перечислять нужные.
@admin.register(Movie)
class MovieAdmin(admin.ModelAdmin):
exclude = ['is_published']Теперь поле is_published не отображается, а другие появятся автоматически.
⚠ Важно: нельзя одновременно использовать
fieldsиexclude. Django выбросит ошибку.
Это полезно когда:
- поле не должно вручную редактироваться,
- оно вычисляется автоматически,
- администратор не должен его менять.
Например, поле slug.
@admin.register(Movie)
class MovieAdmin(admin.ModelAdmin):
fields = ['title', 'slug', 'description', 'year', 'genre']
readonly_fields = ['slug']Теперь slug виден, но изменить его нельзя.
Существует два подхода:
Изменяем модель Movie.
movies/models.py:
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super().save(*args, **kwargs)- работает везде: админка, API, тесты, shell;
- защищает от пустого slug.
- может не понравиться, если slug должен заполняться вручную.
Это работает только на странице добавления записи.
movies/admin.py:
@admin.register(Movie)
class MovieAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("title",)}Django автоматически подставляет slug при вводе заголовка.
- Открываем добавление нового фильма.
- Печатаем название.
- Смотрим, как поле slug заполняется автоматически.
По умолчанию Django показывает поле actors в виде списка, где нужно выделять элементы с Ctrl.
Это неудобно при большом количестве актёров.
Есть два варианта улучшения:
class MovieAdmin(admin.ModelAdmin):
fields = ['title', 'slug', 'description', 'year', 'genre', 'actors']
filter_horizontal = ['actors']Теперь у администратора появится:
- левый список — доступные актёры,
- правый список — выбранные,
- кнопки добавления/удаления.
filter_vertical = ['actors']То же самое, но блоки располагаются вертикально.
- Перейдите в редактирование фильма.
- Найдите поле «actors».
- Убедитесь, что интерфейс стал удобнее.
Причина: указаны одновременно fields и exclude.
✔ Решение: оставить только одно.
Причина: обязательное поле не включено в форму.
✔ Решение: добавить его в fields или убрать из exclude.
Причина: не указан prepopulated_fields.
✔ Решение: добавить
prepopulated_fields = {"slug": ("title",)}from django.contrib import admin
from .models import Movie, Genre, Actor
@admin.register(Movie)
class MovieAdmin(admin.ModelAdmin):
fields = ['title', 'slug', 'description', 'year', 'genre', 'actors']
readonly_fields = ['slug']
prepopulated_fields = {"slug": ("title",)}
filter_horizontal = ['actors']
@admin.register(Genre)
class GenreAdmin(admin.ModelAdmin):
search_fields = ['name']
@admin.register(Actor)
class ActorAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("name",)}-
Убрать поле из формы. Сделайте так, чтобы поле
is_publishedне отображалось при редактировании фильма. -
Сделать поле
yearтолько для чтения. Сделайте полеyearдоступным только для просмотра. -
Добавить автозаполнение slug у актёра. Используйте
prepopulated_fields. -
Улучшить интерфейс выбора актёров. Поменяйте отображение поля
ManyToManyна удобный фильтр. -
Изменить порядок полей в форме фильма. Порядок должен быть: title, year, genre, description, slug.
- Убрать поле из формы.
class MovieAdmin(admin.ModelAdmin):
exclude = ['is_published']- Сделать поле
yearтолько для чтения.
readonly_fields = ['year']- Добавить автозаполнение slug у актёра.
class ActorAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("name",)}- Улучшить интерфейс выбора актёров.
filter_horizontal = ['actors']- Изменить порядок полей в форме фильма.
fields = ['title', 'year', 'genre', 'description', 'slug']- Для чего используется атрибут
fieldsв классе ModelAdmin? - Почему нельзя использовать
fieldsиexcludeодновременно? - Что делает атрибут
readonly_fields? - В каких случаях стоит использовать автозаполнение slug?
- Чем
prepopulated_fieldsотличается от ручного автогенератора slug в модели? - Как улучшить интерфейс для выбора значений Many-to-Many?
- Почему может появиться ошибка «This field is required»?
- Где Django берёт порядок полей формы, если
fieldsне указан? - Как сделать поле видимым, но не редактируемым?
- Для чего назначают
filter_verticalилиfilter_horizontal?