Django-Admin.ru

Переопределение шаблонов и стилей Django Admin

Как изменить внешний вид Django Admin без патчинга ядра: base_site.html, блоки branding и extrastyle, CSS-переменные, тёмная тема и шаблоны моделей.

Редакция Django-Admin.ru
· обновлено · 10 мин чтения

Содержание · 12 разделов
  1. Что можно изменить без шаблонов
  2. Как Django находит шаблоны админки
  3. Какие шаблоны и блоки переопределять
  4. Логотип и название в шапке
  5. Цвета через CSS-переменные
  6. Тёмная тема
  7. Изменения для одной модели
  8. Статика в продакшне
  9. Чего делать не нужно
  10. Частые ошибки
  11. Шаблон не применяется
  12. Стили не применяются
  13. В тёмной теме остались светлые цвета
  14. Пропали стили страницы списка или дашборда
  15. Частые вопросы
  16. Как заменить фавикон админки?
  17. Как изменить страницу входа?
  18. Как добавить свою ссылку в шапку?
  19. Как отличить тестовую админку от рабочей?
  20. Итог

Внешний вид Django Admin меняется без правки исходников Django. Шаблоны админки можно переопределить по одному, а цвета задаются CSS-переменными. Достаточно положить свои файлы туда, где Django найдёт их раньше встроенных.

Коротко: создайте templates/admin/base_site.html со строкой {% extends "admin/base_site.html" %}. В блоке branding замените логотип и название, а в блоке extrastyle подключите свой CSS, где переопределены переменные --primary, --secondary, --header-bg и другие. Примеры проверены на Django 5.2 LTS и 6.x; отличия Django 4.2 отмечены отдельно.

Что можно изменить без шаблонов

Тексты в шапке и заголовках задаются атрибутами сайта админки. Шаблоны для этого не нужны:

# shop/admin.py (любой admin.py проекта)
from django.contrib import admin

admin.site.site_header = "Acme: управление"  # шапка и страница входа
admin.site.site_title = "Acme Admin"  # окончание <title> во вкладке
admin.site.index_title = "Разделы"  # заголовок главной страницы
admin.site.site_url = "https://acme.example/"  # ссылка «Открыть сайт»
АтрибутГде виденПо умолчанию
site_headerшапка всех страниц, форма входа«Администрирование Django»
site_titleзаголовок вкладки браузера«Административный сайт Django»
index_titleH1 на главной странице«Администрирование сайта»
site_urlссылка «Открыть сайт», None скрывает её"/"
enable_nav_sidebarбоковая панель с моделямиTrue

Если этого мало, переходите к шаблонам.

Как Django находит шаблоны админки

Встроенные шаблоны лежат в django/contrib/admin/templates/admin/. Django ищет шаблон по имени в таком порядке:

  1. каталоги из TEMPLATES["DIRS"];
  2. папки templates/ приложений в порядке INSTALLED_APPS (при APP_DIRS: True).

Отсюда два рабочих варианта. Первый: общая папка проекта.

# settings.py
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],  # здесь лежит templates/admin/...
        "APP_DIRS": True,
        "OPTIONS": {"context_processors": [...]},
    },
]

Второй: шаблоны лежат в приложении admin_theme/templates/admin/, и это приложение стоит выше django.contrib.admin:

INSTALLED_APPS = [
    "admin_theme",  # должен быть раньше django.contrib.admin
    "django.contrib.admin",
    "django.contrib.auth",
    # ...
]

Свой шаблон почти никогда не нужно копировать целиком. Начните файл с {% extends "admin/base_site.html" %}, даже если он сам называется admin/base_site.html. Django пропустит текущий файл и унаследует встроенный, а вы переопределите только нужные блоки. Так обновления Django не ломают вашу тему.

Какие шаблоны и блоки переопределять

Всё оформление админки наследуется от admin/base.html через admin/base_site.html. Основные блоки:

БлокШаблонДля чего
brandingbase_site.htmlлоготип и название в шапке
titlebase_site.htmlсодержимое <title>
extrastylebase.htmlдополнительные CSS после стандартных
extraheadbase.htmlскрипты, мета-теги, шрифты
dark-mode-varsbase.htmlподключение dark_mode.css и theme.js
userlinksbase.htmlссылки справа в шапке: документация, смена пароля, выход
nav-globalbase_site.htmlпустое место в шапке под свою навигацию
footerbase.htmlподвал страницы

Шаблоны страниц можно переопределить не только для всей админки, но и для отдельного приложения или модели. Django ищет, например, сначала admin/<app_label>/<model_name>/change_form.html, затем admin/<app_label>/change_form.html и только потом admin/change_form.html. Так работают change_form.html, change_list.html, delete_confirmation.html, object_history.html, app_index.html, а также фрагменты submit_line.html, pagination.html, search_form.html, actions.html и другие. Шаблоны index.html и login.html переопределяются только для всего сайта.

Логотип и название в шапке

Переопределите блок branding. Разметку берите из base_site.html вашей версии Django и меняйте только нужное:

{# templates/admin/base_site.html #}
{% extends "admin/base_site.html" %}
{% load static %}

{% block extrastyle %}{{ block.super }}
<link rel="stylesheet" href="{% static 'admin_theme/brand.css' %}">
{% endblock %}

{% block branding %}
<div id="site-name">
  <a href="{% url 'admin:index' %}" class="brand">
    <img src="{% static 'admin_theme/logo.svg' %}" alt="" width="28" height="28">
    {{ site_header|default:_('Django administration') }}
  </a>
</div>
{% if user.is_anonymous %}
  {% include "admin/color_theme_toggle.html" %}
{% endif %}
{% endblock %}

{% block footer %}
<div class="brand-footer">Acme · <a href="mailto:it@example.com">поддержка</a></div>
{% endblock %}

Обратите внимание на детали:

  • {% load static %} нужен в каждом шаблоне, где есть тег static: загрузка из родительского шаблона не наследуется.
  • Фильтр default в site_header нужен для страниц, которые используют шаблоны админки без её контекста. Например, это стандартные страницы сброса пароля.
  • Переключатель темы для неавторизованного пользователя (страница входа) в оригинале выводится внутри branding. Если его не повторить, он пропадёт со страницы входа.
  • В Django 4.2 название обёрнуто в <h1 id="site-name">, с Django 5.0 — в <div id="site-name">. Стили админки привязаны к этой разметке, поэтому сверяйтесь со своей версией.

Логотип и CSS кладите в static/ приложения, например в admin_theme/static/admin_theme/. Не называйте свою папку admin/, чтобы не перекрыть встроенную статику.

Цвета через CSS-переменные

С Django 3.2 стили админки построены на CSS-переменных. Их значения объявлены в начале файла django/contrib/admin/static/admin/css/base.css. Перекрасить админку — значит переопределить переменные в своём файле, который подключается в extrastyle после стандартных стилей:

/* admin_theme/static/admin_theme/brand.css */
html[data-theme='light'],
:root {
  --primary: #a5b4fc;
  --secondary: #3730a3;
  --accent: #fde68a;
  --primary-fg: #fff;

  --header-bg: #1e1b4b;
  --header-color: #e0e7ff;
  --breadcrumbs-bg: #312e81;

  --link-fg: #4338ca;
  --link-hover-color: #312e81;

  --button-bg: #3730a3;
  --button-hover-bg: #312e81;
  --default-button-bg: #4f46e5;
  --default-button-hover-bg: #3730a3;

  --font-family-primary: 'Inter', system-ui, sans-serif;
}

#site-name .brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

Селектор html[data-theme='light'], :root повторяет тот, что использует сама Django. Почему это важно, разберём в разделе о тёмной теме.

Ключевые переменные:

ПеременнаяЧто окрашивает
--primaryсветлый фирменный цвет, фоны выделенных элементов
--secondaryосновной тёмный цвет; в Django 5.x от него по умолчанию считаются шапка и кнопки
--accentссылка с названием сайта в шапке
--header-bg, --header-colorфон и текст шапки
--breadcrumbs-bgфон «хлебных крошек»
--link-fg, --link-hover-colorцвет ссылок
--button-bg, --default-button-bgобычные кнопки и главная кнопка «Сохранить»
--body-bg, --body-fgфон и текст страницы
--font-family-primaryосновной шрифт

Важно: связи между переменными меняются от версии к версии. Например, заголовки модулей (.module h2) в Django 4.2 окрашены через --primary, а в 5.x — через --header-bg. Перед настройкой откройте base.css своей версии и найдите, где используется нужная переменная.

Шрифт из --font-family-primary нужно ещё подключить. Для этого добавьте <link> на файл шрифта в блок extrahead или extrastyle.

Тёмная тема

Тёмная тема появилась в Django 3.2. С версии 4.2 в шапке есть переключатель «светлая / тёмная / как в системе». Устроено это так:

  • файл admin/css/dark_mode.css подключается в блоке dark-mode-vars и переопределяет переменные внутри @media (prefers-color-scheme: dark) и для html[data-theme="dark"];
  • скрипт theme.js хранит выбор пользователя в localStorage и ставит на <html> атрибут data-theme со значением auto, light или dark.

Ваш brand.css загружается позже dark_mode.css. Правило :root с теми же переменными перебьёт тёмные значения, и при системной тёмной теме админка останется светлой. Поэтому задайте тёмные значения явно, в тех же двух местах, что и Django:

/* продолжение brand.css */
@media (prefers-color-scheme: dark) {
  :root {
    --primary: #312e81;
    --link-fg: #a5b4fc;
    --link-hover-color: #c7d2fe;
  }
}

html[data-theme='dark'] {
  --primary: #312e81;
  --link-fg: #a5b4fc;
  --link-hover-color: #c7d2fe;
}

Благодаря селектору html[data-theme='light'] в светлом блоке явный выбор «светлая» побеждает тёмный медиазапрос: у этого селектора выше специфичность, чем у :root.

Если тёмная тема не нужна вовсе, отключите её двумя изменениями. Добавьте в templates/admin/base_site.html пустой блок {% block dark-mode-vars %}{% endblock %} и создайте пустой файл templates/admin/color_theme_toggle.html, чтобы убрать кнопку переключения.

Изменения для одной модели

Предупреждение на форме оплаченного заказа не требует трогать другие страницы. Создайте templates/admin/shop/order/change_form.html, где shop — метка приложения, а order — имя модели в нижнем регистре:

{% extends "admin/change_form.html" %}

{% block form_top %}
{{ block.super }}
{% if original.status == "paid" %}
  <p class="errornote">Заказ уже оплачен: изменение суммы не вернёт деньги покупателю.</p>
{% endif %}
{% endblock %}

В контексте формы изменения объект доступен как original. Вместо соглашения об именах можно явно указать шаблон в ModelAdmin:

@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
    change_form_template = "admin/shop/order_change_form.html"

Так же работают change_list_template, add_form_template, delete_confirmation_template и object_history_template. У прокси-модели свой model_name, поэтому шаблон основной модели к ней не применяется. Пример с графиком в шаблоне списка разобран в статье «Графики и отчёты в Django Admin», а виджеты на главной странице — в материале про дашборд Django Admin.

Статика в продакшне

Новые CSS и картинки попадут на сервер только после python manage.py collectstatic. Если стили не обновляются у пользователей после выкладки, виноват кеш браузера. Хранилище ManifestStaticFilesStorage добавляет хеш содержимого к имени файла (brand.3f2a….css), и браузер получает новую версию при каждом изменении:

# settings.py (Django 4.2+)
STORAGES = {
    "default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
    "staticfiles": {
        "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
    },
}

Как раздавать статику через Nginx, описано в руководстве по деплою Django с Gunicorn и Nginx.

Чего делать не нужно

  • Править файлы в site-packages/django/. Изменения пропадут при первом же pip install -U django и не попадут на другие машины.
  • Копировать base.html целиком. Вы зафиксируете разметку старой версии и потеряете исправления из новых. Переопределяйте блоки через extends.
  • Подменять admin/css/base.css файлом с тем же путём. Технически это работает, но тянет те же проблемы, что и копирование шаблонов. Достаточно переопределить переменные.
  • Ставить !important повсюду. Ваш CSS и так подключается последним. Если правило не срабатывает, проверьте специфичность селектора.

Если перекраски мало и нужен другой интерфейс с боковым меню, вкладками и дашбордом, посмотрите на готовые темы. Их сравнение — в обзоре Unfold, Jazzmin и Grappelli.

Частые ошибки

Шаблон не применяется

Проверьте путь templates/admin/base_site.html: подпапка admin/ обязательна. Если шаблон лежит в приложении, оно должно стоять в INSTALLED_APPS выше django.contrib.admin. Если в папке проекта, она должна быть указана в TEMPLATES["DIRS"]. После создания нового каталога шаблонов перезапустите сервер.

Стили не применяются

Откройте вкладку Network в инструментах разработчика. 404 на brand.css означает неверный путь в {% static %} или забытый collectstatic. Если файл загружается, но цвет не меняется, найдите в base.css своей версии, какая переменная на самом деле отвечает за этот элемент.

В тёмной теме остались светлые цвета

Вы переопределили переменные только в :root. Добавьте тёмные значения в @media (prefers-color-scheme: dark) и в html[data-theme='dark'], как в примере выше.

Пропали стили страницы списка или дашборда

Вы переопределили extrastyle без {{ block.super }}. В base_site.html это безопасно, потому что в base.html блок пуст. Но в change_list.html, change_form.html и index.html через этот блок подключаются стили страницы, и без {{ block.super }} они пропадут.

Частые вопросы

Как заменить фавикон админки?

Добавьте ссылку на иконку в блок extrahead того же templates/admin/base_site.html:

{% block extrahead %}{{ block.super }}
<link rel="icon" href="{% static 'admin_theme/favicon.svg' %}" type="image/svg+xml">
{% endblock %}

Как изменить страницу входа?

Логотип и стили из base_site.html применяются и к ней. Чтобы добавить текст, создайте templates/admin/login.html и расширьте блок content:

{# templates/admin/login.html #}
{% extends "admin/login.html" %}

{% block content %}
<p class="help">Вход только для сотрудников Acme. Забыли пароль — напишите на it@example.com.</p>
{{ block.super }}
{% endblock %}

Как добавить свою ссылку в шапку?

Переопределите блок userlinks и сохраните стандартные ссылки через {{ block.super }}:

{% block userlinks %}
<a href="https://wiki.example.com/admin-help">Инструкция</a> /
{{ block.super }}
{% endblock %}

Как отличить тестовую админку от рабочей?

Проще всего — разные заголовки. Задайте site_header по переменной окружения, а для заметности добавьте в тестовом окружении отдельный CSS с другим --header-bg:

# shop/admin.py
import os

from django.contrib import admin

env = os.environ.get("APP_ENV", "production")
if env != "production":
    admin.site.site_header = f"Acme: {env.upper()}"

Итог

Внешний вид Django Admin меняется в трёх местах. Атрибуты site_header и site_title отвечают за тексты, переопределённый admin/base_site.html с блоками branding и extrastyle — за логотип и подключение стилей, CSS-переменные — за цвета светлой и тёмной темы. Для отдельных моделей используйте шаблоны вида admin/<app>/<model>/change_form.html.

Остальные настройки админки собраны в полном руководстве по Django Admin. Все статьи раздела — на странице Django Admin.

  • #Django Admin
  • #Шаблоны Admin
  • #CSS
  • #Темизация
  • #Кастомизация Admin
↑ ↓ выборEnter открытьEsc закрыть