paper-admin
Custom Django admin interface based on Bootstrap 4.
Requirements
- Python >= 3.6
- Django >= 2.2
Table of Contents
- Installation
- Patches
- Badge
- Admin menu
- Reorderable drag-and-drop lists
- Form tabs
- Stylization
- Settings
- Additional References
Installation
Install the latest release with pip:
pip install paper-admin
Add paper_admin
to your INSTALLED_APPS setting before django.contrib.admin
.
INSTALLED_APPS = [
"paper_admin",
"paper_admin.patches.dal", # optional
"paper_admin.patches.django_solo", # optional
"paper_admin.patches.mptt", # optional
"paper_admin.patches.logentry_admin", # optional
"paper_admin.patches.tree_queries", # optional
# ...
"django.contrib.admin",
# ...
]
Patches
Некоторые сторонние библиотеки переопределяют стандартные
шаблоны Django и в рамках интерфейса paper_admin
выглядят инородно. По этой причине (а также для внедрения
дополнительного функционала) применяются патчи.
В состав paper_admin
включены следующие патчи:
-
paper_admin.patches.dal
Исправляет стили виджетов django-autocomplete-light -
paper_admin.patches.django_solo
Исправляет хлебные крошки в django-solo. -
paper_admin.patches.mptt
Адаптация django-mptt. Добавляет возможность сортировки узлов дерева (при указании свойстваsortable
). -
paper_admin.patches.logentry_admin
Исправление фильтров и скрытие ненужных кнопок в django-logentry-admin. -
paper_admin.patches.tree_queries
Добавление возможности сортировки узлов дерева для django-tree-queries. Необходимо использовать специальный классTreeNodeModelAdmin
вместоModelAdmin
:# admin.py from django.contrib import admin from paper_admin.patches.tree_queries.admin import TreeNodeModelAdmin # <-- from .models import MyTreeNode @admin.register(MyTreeNode) class MyTreeNodeAdmin(TreeNodeModelAdmin): ... sortable = "position"
Note: как правило, патчи должны быть указаны в INSTALLED_APPS
до библиотек,
которые они исправляют.
Badge
Полоса с текстом в сайдбаре.
Её основное предназначение - визуально обозначить окружение, в котором работает административный интерфейс. Так вы не перепутаете сервер разработки с продашеном.
Цвет полосы и текст устанавливаются в settings.py
:
PAPER_ENVIRONMENT_NAME = "development"
PAPER_ENVIRONMENT_COLOR = "#FFFF00"
Admin menu
Меню в сайдбаре настраивается путем заполнения списка PAPER_MENU
в settings.py
:
from django.utils.translation import gettext_lazy as _
PAPER_MENU = [
dict( # Пункт меню для главной страницы
label=_("Dashboard"),
url="admin:index",
icon="fa fa-fw fa-lg fa-area-chart",
),
dict( # Приложение app с перечнем его моделей
app="app",
icon="fa fa-fw fa-lg fa-home",
models=[
"Tag",
"Category",
"SubCategory",
]
),
"-", # Разделитель
"auth", # Приложение auth
dict( # Модель LogEntry из приложения admin
label=_("Logs"),
icon="fa fa-fw fa-lg fa-history",
perms="admin.view_logentry",
models=[
"admin.LogEntry"
]
),
]
Пункт меню может быть задан одним из четырех способов:
-
Имя приложения.
PAPER_MENU = [ # ... "app", # ... ]
Все модели выбранного приложения образуют подменю. Порядок моделей Django определяет автоматически.
-
Путь к модели.
PAPER_MENU = [ # ... "app.Tag", # ... ]
Создаст пункт меню с заголовком, соответствующим названию модели и ссылающийся на страницу changelist.
-
Строка-разделитель.
PAPER_MENU = [ # ... "-", # ... ]
Добавляет горизонтальную линию. С помощью разделителей можно визуально группировать пункты меню.
-
Словарь.
from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ PAPER_MENU = [ # ... dict( app="app", icon="fa fa-fw fa-lg fa-home", models=[ "Tag", # Модель app.Tag "Category", # Модель app.Category dict( # Произвольный вложенный пункт label=_("Index"), url=reverse_lazy("admin:app_list", kwargs={ "app_label": "app" }) ), ] ), # ... ]
Самый гибкий способ создания пункта меню. В словаре можно явным образом указать название пункта меню, его URL, иконку, CSS-классы и вложенные пункты. Вложенные пункты тоже могут быть заданы с помощью словаря.
При использовании словаря можно указать следующие ключи:
label
:str
- заголовок пункта меню.url
:str
- URL или имя URL-шаблона (например,app:index
).icon
:str
- CSS-классы иконки (используется FontAwesome v4).classes
:str
- CSS-классы пункта меню.perms
:str/list/callable
- права, необходимые для отображения пункта. Для определения суперюзера и сотрудников, можно использовать специальные значенияsuperuser
иstaff
соответственно.app
:str
- имя приложения. Неявно добавляется к именам моделей, указанным в пунктеmodels
.models
:list/dict
- дочерние пункты меню. Содержит список имен моделей приложения или вложенных пунктов, которые можно задать в виде словаря.
Menu item permissions
С помощью параметра perms
можно указать названия прав (Django permissions),
которые должен иметь пользователь, чтобы увидеть соответствующий пункт меню.
На доступность страниц параметр
perms
никак не влияет! Если пользователь знает адрес страницы или ссылка на неё имеется где-то ещё, то пользователь сможет на неё зайти!
PAPER_MENU = [
# Пункт меню приложения app увидят только сотрудники (staff),
# имеющие право на изменение модели `app.Tag`.
dict(
app="app",
perms=["staff", "app.change_tag"],
models=[
"Tag",
]
)
]
Reorderable drag-and-drop lists
Для того, чтобы экземпляры модели можно было сортировать в интерфейсе администратора, необходимо выполнить два условия.
-
Добавить в модель числовое поле, которое будет хранить порядковый номер.
# models.py from django.db import models class MyModel(models.Model): position = models.IntegerField( "position", default=0 )
-
Указать название этого поля в свойстве
sortable
соответствующего подклассаModelAdmin
:# admin.py from django.contrib import admin class MyModelAdmin(admin.ModelAdmin): sortable = "position" # ...
Результат:
Сохранение сортировки происходит через AJAX-запрос.
Таким же путём можно включить сортировку инлайн-форм, но в этом случае сортировка происходит с помощью кнопок и сохраняется вместе со всей страницей при нажатии кнопки "Save":
from django.contrib import admin
class TablularInline(admin.TabularInline):
sortable = "position"
# ...
Результат:
Сортировка paper-admin
совместима с django-mptt
(если в INSTALLED_APPS
добавлен патч paper_admin.patches.mptt
).
Менять местами можно только те элементы, которые имеют общего родителя и находятся на одном уровне
вложенности:
Form tabs
Форму добавления/редактирования объекта можно разделить на вкладки.
Список вкладок указывается в атрибуте tabs
:
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
class TablularInlines(admin.TabularInline):
# имя вкладки, на которой должен быть отображен формсет
tab = 'inlines-tab'
@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
fieldsets = (
(_('First Section'), {
'tab': 'common-tab',
'fields': (
# ...
),
}),
(_('Second Section'), {
'tab': 'common-tab',
'fields': (
# ...
)
}),
(_('Links'), {
'tab': 'links-tab',
'fields': (
# ...
)
}),
)
tabs = [
('common-tab', _('General')),
('links-tab', _('Links')),
('inlines-tab', _('Inlines')),
]
inlines = (TablularInlines, )
Результат:
Вкладки можно добавлять динамически, с помощью метода get_tabs
:
from django.contrib import admin
from .models import Page
@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
def get_tabs(self, request, obj=None):
return [
# ...
]
Stylization
Fieldset
Django даёт возможность
указать произвольные CSS-классы и описание для любого fieldset.
paper-admin
предоставляет набор готовых CSS-классов для стилизации fieldset:
paper-card--primary
paper-card--secondary
paper-card--info
paper-card--danger
paper-card--success
paper-card--warning
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
fieldsets = (
(_("Info Section"), {
"classes": ("paper-card--info", ),
"description": _("Description for the fieldset"),
"fields": (
# ...
),
}),
(_("Success Section"), {
"classes": ("paper-card--success",),
"fields": (
# ...
)
}),
(_("Danger Section"), {
"classes": ("paper-card--danger",),
"fields": (
# ...
)
}),
)
Результат:
Table rows
Для каждого ряда таблицы вызывается метод get_row_classes
, который должен вернуть
список CSS-классов, которые будут добавлены к тэгу <tr>
.
from django.contrib import admin
from .models import Page
@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
def get_row_classes(self, request, obj):
if obj.name.startswith("M"):
return ["table-success"]
elif obj.name.startswith("P"):
return ["table-info"]
return []
Результат:
Inline forms
Inline-формам тоже можно назначить произвольные CSS-классы с помощью метода
get_form_classes
:
from django.contrib import admin
class StackedInline(admin.StackedInline):
def get_form_classes(self, request, obj):
if obj.name.startswith("P"):
return ["paper-card--success"]
elif obj.name.startswith("M"):
return ["paper-card--info"]
return []
class TablularInlines(admin.TabularInline):
def get_form_classes(self, request, obj):
if obj.name.startswith("P"):
return ["table-success"]
elif obj.name.startswith("M"):
return ["table-info"]
return []
Результат:
Settings
PAPER_FAVICON
Путь к favicon-файлу, который будет использоваться в интерфейсе админиcтратора.
Default: "paper_admin/dist/assets/default_favicon.png"
PAPER_ENVIRONMENT_NAME
Текст на бейжде в сайдбаре.
Default: ""
PAPER_ENVIRONMENT_COLOR
Цвет фона бейджа.
Default: ""
PAPER_MENU
Меню.
Default: None
PAPER_MENU_DIVIDER
При встрече указанной строки в списке пунктов PAPER_MENU
,
на её место будут вставлен горизонтальный разделитель.
Default: "-"
PAPER_MENU_PERM_STAFF
Ключевое слово в параметре perms
пункта меню PAPER_MENU
,
которое указывает, что текущий пункт меню должен быть показан
только при условии, что у пользователя установлен флаг is_staff
.
Default: "staff"
PAPER_MENU_PERM_SUPERUSER
Ключевое слово в параметре perms
пункта меню PAPER_MENU
,
которое указывает, что текущий пункт меню должен быть показан
только при условии, что у пользователя установлен флаг is_superuser
.
Default: "superuser"
PAPER_MENU_HIDE_SINGLE_CHILD
При значении True
, те пункты меню, которые содержат единственный
подпункт, не будут отображаться как выпадающие списки. Вместо этого
они сразу будут вести на страницу, указанную в подпункте.
Default: True
PAPER_DEFAULT_TAB_NAME
Алиас вкладки по-умолчанию.
Default: "general"
PAPER_DEFAULT_TAB_TITLE
Заголовок вкладки по-умолчанию.
Default: _("General")
PAPER_LOCALE_PACKAGES
Список модулей, из которых должны загружаться переводы
для JavaScriptCatalog
интерфейса администратора.
Default: ["paper_admin", "django.contrib.admin"]