Настройка админки Django
Автоматически генерируемый админский сайт в Django является одним из самых сильных мест фреймворка. Централизованный интерфейс администратора позволяет легко просматривать данные модели и манипулировать ими. Это позволяет сэкономить массу времени при разработке и управлении контентом.
Несмотря на то, что админка сайта очень хорошо настраивается, многие разработчики не знают всех ее возможностей. Это приводит к тому, что разработчики создают представления и функции для вещей, которые можно было бы легко реализовать с помощью небольшой доработки сайта администратора.
В этой статье на практических примерах мы рассмотрим, как настроить админку сайта Django. Мы рассмотрим встроенные возможности настройки, а также настройку с помощью сторонних пакетов, таких как DjangoQL, django-import-export и django-admin-interface.
Цели
К концу этой статьи вы сможете:
- Выполнить базовую конфигурацию сайта администратора Django
- Объясните, как атрибуты модели Django влияют на админку сайта
- Использование
list_display
для управления отображением полей модели - Добавлять пользовательские поля в
list_display
и форматировать существующие - Добавить ссылки на связанные объекты модели в
list_display
- Возможность поиска и фильтров через
search_fields
иlist_filter
- Работа с инлайнами модели для отношений
N:1
иM:M
- Использование действий администратора Django и создание собственных действий
- Определять формы и шаблоны администратора Django
- Использование DjangoQL для расширенного поиска
- Импорт данных в различные форматы и экспорт данных в различные форматы с помощью
django-import-export
- Изменение внешнего вида админки сайта с помощью
django-admin-interface
Настройка проекта
Для демонстрации различных вариантов настройки сайта администратора я подготовил простое веб-приложение. Это веб-приложение служит системой продажи билетов на мероприятия. Оно позволяет управлять местами проведения мероприятий, концертами, категориями концертов и билетами.
У него следующая модель отношений между сущностями:
Я настоятельно рекомендую вам сначала ознакомиться с этим веб-приложением. После прочтения вы сможете применить полученные знания в своих Django-проектах.
Сначала возьмите исходный код из репозитория на GitHub:
$ git clone https://github.com/duplxey/django-admin-customization.git --branch base
$ cd django-admin-customization
Создайте виртуальную среду и активизируйте ее:
$ python3.11 -m venv env && source env/bin/activate
Установите требования и выполните миграцию базы данных:
(venv)$ pip install -r requirements.txt
(venv)$ python manage.py migrate
Создайте суперпользователя и заполните базу данных:
(venv)$ python manage.py createsuperuser
(venv)$ python manage.py populate_db
Запуск сервера разработки:
(venv)$ python manage.py runserver
Откройте ваш любимый веб-браузер и перейдите по адресу http://localhost:8000/admin. Попробуйте использовать учетные данные суперпользователя для доступа к сайту администратора Django. После этого убедитесь, что база данных заполнена несколькими площадками, категориями концертов, концертами и билетами.
Прежде чем продолжить, я предлагаю вам проверить модели в tickets/models.py. Обратите внимание на то, какие поля имеет модель и как модели связаны между собой.
Основная настройка сайта администратора
На сайте администратора Django предусмотрены некоторые базовые опции конфигурации. С их помощью можно изменить название сайта, заголовок, URL сайта и т.д. Настройка site_header
может быть особенно удобна, если у вас есть несколько окружений и вы хотите легко различать их.
Настройки admin.site
обычно изменяются в основном файле проекта urls.py.
Переименуйте администратора Django в "TicketPlus" и отметьте текущее окружение как dev
:
# core/urls.py
admin.site.site_title = "TicketsPlus site admin (DEV)"
admin.site.site_header = "TicketsPlus administration"
admin.site.index_title = "Site administration"
Все настройки можно посмотреть, обратившись к файлу Django contrib/admin/sites.py.
Другое, что следует сделать, это изменить URL /admin
по умолчанию. Это затруднит злоумышленникам поиск вашей административной панели.
Измените свой core/urls.py следующим образом:
# core/urls.py
urlpatterns = [
path("secretadmin/", admin.site.urls),
]
Теперь ваш административный сайт должен быть доступен по адресу http://localhost:8000/secretadmin.
Django Model и Admin
Некоторые атрибуты модели Django напрямую влияют на работу администратора сайта Django. Наиболее важные:
__str__()
используется для определения отображаемого имени объекта- Класс
Meta
используется для задания различных параметров метаданных (например,ordering
иverbose_name
)
Вот пример использования этих атрибутов на практике:
# tickets/models.py
class ConcertCategory(models.Model):
name = models.CharField(max_length=64)
description = models.TextField(max_length=256, blank=True, null=True)
class Meta:
verbose_name = "concert category"
verbose_name_plural = "concert categories"
ordering = ["-name"]
def __str__(self):
return f"{self.name}"
- Мы предоставили форму множественного числа, поскольку множественное число "категории концертов" не является "категориями концертов".
- Благодаря атрибуту
ordering
категории теперь упорядочены по названию.
Для получения информации обо всех опциях класса
Meta
обратитесь к разделу "Мета-опции модели".
Настройка сайта администратора с помощью класса ModelAdmin
В этом разделе мы рассмотрим, как использовать класс ModelAdmin для настройки администратора сайта.
Отображение списка управления
Атрибут list_display позволяет управлять тем, какие поля модели отображаются на странице списка моделей. Еще одним его достоинством является возможность отображения связанных полей модели с помощью оператора __
.
Далее устанавливаем ConcertAdmin
на list_display
:
# tickets/admin.py
class ConcertAdmin(admin.ModelAdmin):
list_display = ["name", "venue", "starts_at", "price", "tickets_left"]
readonly_fields = ["tickets_left"]
Дождитесь обновления сервера и посмотрите страницу списка концертов в админке.
Новый список выглядит отлично, но есть проблема. Добавив место проведения концерта в list_display
, мы создали проблему N + 1. Поскольку Django необходимо получить название места проведения концерта для каждого концерта в отдельности, будет выполнено гораздо больше запросов.
Для того чтобы избежать проблемы N + 1, можно использовать атрибут list_select_related, который работает аналогично методу select_related:
# tickets/admin.py
class ConcertAdmin(admin.ModelAdmin):
list_display = ["name", "venue", "starts_at", "price", "tickets_left"]
list_select_related = ["venue"]
readonly_fields = ["tickets_left"]
Чтобы узнать больше о производительности Django, а также о N + 1, ознакомьтесь с Django Performance Optimization Tips и Automating Performance Testing in Django.
Далее устанавливаем другие ModelAdmin
s' list_display
для мест проведения и билетов:
# tickets/admin.py
class VenueAdmin(admin.ModelAdmin):
list_display = ["name", "address", "capacity"]
class TicketAdmin(admin.ModelAdmin):
list_display = [
"customer_full_name", "concert",
"payment_method", "paid_at", "is_active",
]
list_select_related = ["concert", "concert__venue"] # to avoid N + 1
Отображение пользовательских полей списка
Настройка list_display
также может быть использована для добавления пользовательских полей. Для добавления пользовательского поля необходимо определить новый метод в классе ModelAdmin
.
Добавить поле "Sold Out", которое будет True
, если билетов нет:
# tickets/admin.py
class ConcertAdmin(admin.ModelAdmin):
list_display = ["name", "venue", "starts_at", "tickets_left", "display_sold_out"]
list_select_related = ["venue"]
def display_sold_out(self, obj):
return obj.tickets_left == 0
display_sold_out.short_description = "Sold out"
display_sold_out.boolean = True
Мы использовали short_description
для задания имени колонки и boolean
для указания Django, что эта колонка имеет булево значение. Таким образом, вместо True
и False
Django отображает значок галочки/крестика. Также нам пришлось добавить наш метод display_sold_out
в list_display
.
Далее добавим пользовательское поле с именем display_price
:
# tickets/admin.py
class ConcertAdmin(admin.ModelAdmin):
list_display = [
"name", "venue", "starts_at", "tickets_left", "display_sold_out", "display_price"
]
# ...
def display_price(self, obj):
return f"${obj.price}"
display_price.short_description = "Price"
display_price.admin_order_field = "price"
Мы использовали admin_order_field
, чтобы сообщить Django, по какому полю этот столбец является упорядочиваемым.
Связанные объекты модели
Иногда бывает полезно добавить ссылки на связанные объекты модели вместо того, чтобы просто показывать их отображаемое имя. Чтобы продемонстрировать, как это делается, приведем ссылки на места проведения концертов на странице списка концертов.
Прежде чем это сделать, рассмотрим структуру URL-адресов сайта администратора Django:
Page | URL | Description |
---|---|---|
List | admin:<app>_<model>_changelist |
Displays the list of objects |
Add | admin:<app>_<model>_add |
Object add form |
Change | admin:<app>_<model>_change |
Object change form (requires objectId ) |
Delete | admin:<app>_<model>_delete |
Object delete form (requires objectId ) |
History | admin:<app>_<model>_history |
Displays object's history (requires objectId ) |
Для добавления ссылки на страницу изменения места проведения мероприятия нам необходимо использовать следующий URL:
Format: admin:<app>_<model>_change
Actual: admin:tickets_venue_change
Добавьте метод display_venue
к ConcertAdmin
следующим образом:
# tickets/admin.py
class ConcertAdmin(DjangoQLSearchMixin, admin.ModelAdmin):
list_display = [
"name", "venue", "starts_at", "tickets_left",
"display_sold_out", "display_price", "display_venue",
]
list_select_related = ["venue"]
# ...
def display_venue(self, obj):
link = reverse("admin:tickets_venue_change", args=[obj.venue.id])
return format_html('<a href="{}">{}</a>', link, obj.venue)
display_venue.short_description = "Venue"
Для реверса URL мы использовали метод reverse и передали obj.venue.id
в качестве objectId
.
Не забывайте об импорте:
from django.urls import reverse
from django.utils.html import format_html
Дождитесь обновления сервера разработки и перейдите на страницу со списком концертов. Теперь места проведения концертов должны быть кликабельными.
Объекты модели фильтра
Админка Django позволяет легко фильтровать объекты. Чтобы включить фильтрацию, необходимо указать, какие поля или связанные с ними поля модели должны быть доступны для фильтрации. Самое интересное, что Django умеет складывать фильтры - например, фильтровать по двум и более полям одновременно.
Далее добавьте атрибут list_filter к ConcertAdmin
следующим образом:
# tickets/admin.py
class ConcertAdmin(admin.ModelAdmin):
# ...
list_filter = ["venue"]
Для фильтрации по полям связанного объекта используйте оператор
__
.При выборе фильтров следите за тем, чтобы не включать поля со слишком большим количеством значений. Например,
tickets_left
- неудачный выбор фильтра, поскольку на каждый концерт остается разное количество билетов.
Для более расширенной функциональности фильтрации можно также определить пользовательские фильтры. Для определения пользовательского фильтра необходимо указать опции или так называемые lookups
и queryset
для каждого lookup
.
Например, для фильтрации по тому, продан концерт или нет, создайте SoldOutFilter
и включите его в ConcertAdmin
в list_filters
:
# tickets/admin.py
class SoldOutFilter(SimpleListFilter):
title = "Sold out"
parameter_name = "sold_out"
def lookups(self, request, model_admin):
return [
("yes", "Yes"),
("no", "No"),
]
def queryset(self, request, queryset):
if self.value() == "yes":
return queryset.filter(tickets_left=0)
else:
return queryset.exclude(tickets_left=0)
class ConcertAdmin(admin.ModelAdmin):
# ...
list_filter = ["venue", SoldOutFilter]
Не забудьте про импорт:
from django.contrib.admin import SimpleListFilter
Зайдите на свой административный сайт и убедитесь, что фильтры работают как положено.
Поиск объектов модели
Админка Django предоставляет базовую функциональность поиска. Его можно включить, указав, какие поля модели должны быть доступны для поиска с помощью атрибута search_fields
. Следует помнить, что по умолчанию Django не поддерживает нечеткие запросы.
Давайте сделаем поиск концертов по их названиям, местам проведения и адресам заведений.
Добавьте атрибут search_fields к ConcertAdmin
следующим образом:
# tickets/admin.py
class ConcertAdmin(admin.ModelAdmin):
# ...
search_fields = ["name", "venue__name", "venue__address"]
Подождите, пока сервер обновится и протестирует окно поиска.
Модель рукоятки Inlines
Интерфейс администратора позволяет редактировать модели на той же странице, что и родительская модель, с помощью инлайнов. Django предоставляет два типа инлайнов StackedInline и TabularInline. Основное различие между ними заключается в том, как они выглядят.
Для отображения концертов на странице с подробной информацией о заведении используем инлайн.
Создайте ConcertInline
и добавьте его к VenueAdmin
в inlines
следующим образом:
# tickets/admin.py
class ConcertInline(admin.TabularInline):
model = Concert
fields = ["name", "starts_at", "price", "tickets_left"]
# optional: make the inline read-only
readonly_fields = ["name", "starts_at", "price", "tickets_left"]
can_delete = False
max_num = 0
extra = 0
show_change_link = True
class VenueAdmin(admin.ModelAdmin):
list_display = ["name", "address", "capacity"]
inlines = [ConcertInline]
Зайдите на свой административный сайт и перейдите на страницу с информацией о каком-либо объекте. Прокрутите страницу вниз, и там должен появиться встроенный раздел "Концерты".
Более подробную информацию об инлайнах и о том, как работать с отношениями "многие-ко-многим", можно найти в документах по администрированию сайта Django.
Пользовательские действия администратора
<<<Действия администратора
Django позволяют выполнить "действие" над объектом или группой объектов. С помощью действия можно изменить атрибуты объекта, удалить объект, скопировать его и т.д. Действия используются в основном для часто выполняемых "акций" или массовых изменений.
Прекрасным примером является активация или деактивация билета. Предположим, что у нас есть много билетов, которые мы хотим активировать. Было бы довольно утомительно щелкать по каждому из них, изменять его свойство is_active
и сохранять модель. Вместо этого мы можем определить действие, которое будет делать именно это.
Определите действия activate_tickets
и deactivate_tickets
и добавьте их в TicketAdmin
следующим образом:
# tickets/admin.py
@admin.action(description="Activate selected tickets")
def activate_tickets(modeladmin, request, queryset):
queryset.update(is_active=True)
@admin.action(description="Deactivate selected tickets")
def deactivate_tickets(modeladmin, request, queryset):
queryset.update(is_active=False)
class TicketAdmin(admin.ModelAdmin):
# ...
actions = [activate_tickets, deactivate_tickets]
Откройте еще раз страницу администратора, перейдите к представлению списка билетов, и вы должны увидеть пользовательские действия. Протестируйте их, активируя и деактивируя сразу несколько билетов.
Более подробную информацию о действиях администратора Django можно найти в разделе Admin actions.
Override Django Admin Forms
По умолчанию Django автоматически генерирует ModelForm для вашей модели. Эта форма затем используется на странице добавления и изменения. Если вы хотите настроить форму или реализовать уникальную валидацию данных, вам придется переопределить форму.
Чтобы продемонстрировать это, разобьем customer_full_name
на два поля ввода и выведем радиокнопки вместо выпадающего списка для способов оплаты.
Далее создайте файл forms.py в приложении tickets:
# tickets/forms.py
from django import forms
from django.forms import ModelForm, RadioSelect
from tickets.models import Ticket
class TicketAdminForm(ModelForm):
first_name = forms.CharField(label="First name", max_length=32)
last_name = forms.CharField(label="Last name", max_length=32)
class Meta:
model = Ticket
fields = [
"concert",
"first_name",
"last_name",
"payment_method",
"is_active"
]
widgets = {
"payment_method": RadioSelect(),
}
def __init__(self, *args, **kwargs):
instance = kwargs.get('instance')
initial = {}
if instance:
customer_full_name_split = instance.customer_full_name.split(" ", maxsplit=1)
initial = {
"first_name": customer_full_name_split[0],
"last_name": customer_full_name_split[1],
}
super().__init__(*args, **kwargs, initial=initial)
def save(self, commit=True):
self.instance.customer_full_name = self.cleaned_data["first_name"] + " " \
+ self.cleaned_data["last_name"]
return super().save(commit)
Здесь:
- Мы добавили поля формы
first_name
иlast_name
. - Мы использовали класс
Meta
, чтобы указать, к какой модели относится данная форма и какие поля включать. - На форме
__init__()
мы заполнили форму, используя данные экземпляра модели. - На форме
save()
мы объединилиfirst_name
иlast_name
и сохранили ее какcustomer_full_name
.
Далее устанавливаем TicketAdmin
в form
следующим образом:
# tickets/admin.py
class TicketAdmin(admin.ModelAdmin):
# ...
form = TicketAdminForm
Не забудьте про импорт:
from tickets.forms import TicketAdminForm
Перезапустите сервер разработки и перейдите на страницу с подробной информацией о билете. Если все прошло успешно, то вы должны увидеть, что имя и фамилия теперь находятся в отдельных полях, а способы оплаты используют радиокнопки вместо выпадающего списка.
Override Django Admin Templates
Администрирование сайта Django позволяет настраивать любой его визуальный аспект путем переопределения шаблонов. Для этого достаточно сделать следующее:
- Просмотрите исходный код Django и скопируйте оригинальный шаблон.
- Вставьте шаблон в "templates/admin" или "templates/registration", соответственно.
- Измените шаблон по своему вкусу.
В большинстве случаев можно обойтись лишь изменением части исходного шаблона.
Например, если мы хотим добавить сообщение над формой входа в систему, мы можем наследовать от login.html и затем изменить блок content_title
:
<!-- templates/admin/login.html -->
{% extends "admin/login.html" %}
{% block content_title %}
<p style="background: #ffffcc; padding: 10px 8px">
This is a really important message.
</p>
{% endblock %}
Перейдите на страницу входа в систему, и вы должны увидеть желтое сообщение.
Расширенный поиск с помощью DjangoQL
DjangoQL - это мощный сторонний пакет, позволяющий выполнять расширенные запросы, не опираясь на сырой SQL. Он имеет собственный синтаксис и автодополнение, поддерживает логические операторы и работает для любой модели Django.
Начните с установки пакета:
(env)$ pip install djangoql==0.17.1
Добавить в INSTALLED_APPS
в core/settings.py:
# core/settings.py
INSTALLED_APPS = [
# ...
"djangoql",
]
Далее добавьте DjangoQLSearchMixin
в качестве родительского класса ко всем ModelAdmin
, в которых необходимо включить расширенные возможности поиска.
Добавим его в TicketAdmin
, например:
# tickets/admin.py
class TicketAdmin(DjangoQLSearchMixin, admin.ModelAdmin):
# ...
Не забудьте про импорт:
from djangoql.admin import DjangoQLSearchMixin
Теперь для выполнения расширенных запросов можно использовать ту же поисковую строку, что и раньше. Примеры:
is_active = True
возвращает активные билетыpayment_method = "ET" or payment_method = "BC"
возвращает билеты, купленные за криптовалютуconcert.venue.name ~ "Amphitheatre"
возвращает билеты на концерты в амфитеатрахconcert.tickets_left > 500
возвращает билеты на концерты, на которые осталось более 500 билетов
Для получения дополнительной информации о языке DjangoQL ознакомьтесь с DjangoQL language reference.
Импорт и экспорт данных с помощью Django Import Export
В этом разделе мы рассмотрим, как импортировать и экспортировать данные об объектах с помощью django-import-export, который представляет собой отличный пакет для удобного импорта и экспорта данных в различных форматах, включая JSON, CSV и YAML. Пакет также поставляется со встроенной интеграцией с администратором.
Сначала установите его:
(env)$ pip install django-import-export==3.2.0
Далее добавьте его в INSTALLED_APPS
в core/settings.py:
# core/settings.py
INSTALLED_APPS = [
# ...
"import_export",
]
Соберите статические файлы:
(env)$ python manage.py collectstatic
После этого добавьте ImportExportActionModelAdmin
в качестве родительского класса ко всем ModelAdmin
, которые вы хотите сделать импортируемыми/экспортируемыми.
Вот пример для TicketAdmin
:
# tickets/admin.py
class TicketAdmin(DjangoQLSearchMixin, ImportExportActionModelAdmin):
# ...
Нам пришлось удалить базовый класс
admin.ModelAdmin
, посколькуImportExportActionModelAdmin
уже наследует от него. Включение обоих классов привело бы к ошибке TypeError.
Не забудьте про импорт:
from import_export.admin import ImportExportActionModelAdmin
Если вы хотите, чтобы модель была только экспортируемой, используйте
ExportActionModelAdmin
.
Если вы перейдете на страницу своего тикета, то увидите, что действие экспорта было добавлено. Протестируйте его, выбрав несколько билетов и желаемый формат. Затем нажмите кнопку "Go".
Затем можно протестировать функциональность импорта, импортировав только что экспортированный файл.
Сайт администратора с интерфейсом администратора Django
Настройка внешнего вида администратора сайта через переопределение шаблона может быть неудобной. Вы можете случайно что-то сломать, шаблоны администрирования Django могут измениться в будущем, и это будет хлопотно для поддержки.
Более эффективным подходом к стилизации сайта администратора является использование пакета django-admin-interface. Этот пакет содержит красивые готовые темы интерфейса администратора и позволяет легко настраивать различные аспекты сайта администратора, включая изменение цветов, заголовка, favicon, логотипа и т.д.
Начните с установки через pip:
(env)$ pip install django-admin-interface==0.26.0
Далее добавляем admin_interface
и colorfield
к INSTALLED_APPS
перед django.contrib.admin
:
# core/settings.py
INSTALLED_APPS = [
#...
"admin_interface",
"colorfield",
#...
"django.contrib.admin",
#...
]
X_FRAME_OPTIONS = "SAMEORIGIN" # allows you to use modals insated of popups
SILENCED_SYSTEM_CHECKS = ["security.W019"] # ignores redundant warning messages
Миграция базы данных:
(env)$ python manage.py migrate
Собирать статические файлы:
(env)$ python manage.py collectstatic --clear
Запустите сервер разработки и перейдите по адресу http://localhost:8000/secretadmin. Вы заметите, что ваш сайт администрирования Django выглядит более современно, и на нем появится раздел "Admin Interface".
Нажмите кнопку "Admin Interface > Themes", чтобы увидеть все установленные на данный момент темы. По умолчанию должна быть только одна тема под названием "Django". При желании можно установить еще три темы с помощью фиксов:
(env)$ python manage.py loaddata admin_interface_theme_bootstrap.json
(env)$ python manage.py loaddata admin_interface_theme_foundation.json
(env)$ python manage.py loaddata admin_interface_theme_uswds.json
Нажав на существующую тему, можно настроить все перечисленные ранее аспекты.
Заключение
В этой статье мы рассмотрели многие концепции настройки администратора Django. Теперь вы должны уметь применять эти концепции на практике и адаптировать админку Django под нужды вашего проекта.
Вернуться на верх