Skip to main content

Custom Django admin interface.

Project description

paper-admin

Custom Django admin interface based on Bootstrap 4.

PyPI Build Status

Requirements

  • Python >= 3.6
  • Django >= 2.2

Installation

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.post_office',      # 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.post_office
    Исправление виджета списка email адресов в django-post_office

Note: как правило, патчи должны быть указаны в INSTALLED_APPS до библиотек, которые они исправляют.

Сортировка объектов при помощи Drag & Drop

Для того, чтобы экземпляры модели можно было сортировать в интерфейсе администратора, необходимо выполнить два условия.

  1. Добавить в модель числовое поле, которое будет хранить порядковый номер.

    from django.db import models
    
    class MyModel(models.Model):
        order = models.IntegerField(
            "order", 
            default=0,
            editable=False  # опционально
        )
    
  2. Указать название поля в свойстве sortable.

    from django.contrib import admin
    
    class MyModelAdmin(admin.ModelAdmin):
        sortable = 'order'
        # ...
    

Результат:

https://user-images.githubusercontent.com/6928240/125331456-0f1bb280-e359-11eb-8b17-b04be4b1e62c.mp4

Сохранение сортировки происходит через AJAX-запрос.

Таким же путём можно включить сортировку инлайн-форм, но в этом случае сортировка происходит с помощью кнопок и сохраняется вместе со всей страницей при нажатии кнопки "Save":

from django.contrib import admin

class TablularInline(admin.TabularInline):
    sortable = 'order'
    # ...

Результат:

https://user-images.githubusercontent.com/6928240/125331956-b6004e80-e359-11eb-8422-832dfe37bb6c.mp4

Сортировка paper-admin совместима с django-mptt. Менять местами можно только те элементы, которые имеют общего родителя и находятся на одном уровне вложенности:

https://user-images.githubusercontent.com/6928240/125340277-55760f00-e363-11eb-94d4-49a978cb7ae4.mp4

Вкладки

Форму добавления/редактирования объекта можно разделить на вкладки. Список вкладок указывается в атрибуте tabs:

from django.contrib import admin
from django.utils.translation import gettext_lazy as _


class TablularInlines(admin.TabularInline):
    # имя вкладки, на которой должен быть отображен формсет
    tab = 'common-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, )

Результат:

https://user-images.githubusercontent.com/6928240/125336032-4e003700-e35e-11eb-8399-9cff90ea7aca.mp4

Вкладки можно добавлять динамически, с помощью метода get_tabs:

from django.contrib import admin

@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
    def get_tabs(self, request, obj=None):
        return [
            # ...
        ]

Стилизация

Стилизация 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": (
                # ...
            )
        }),
    )

Результат:

Стилизация рядов таблицы

Для каждого ряда таблицы вызывается метод get_row_classes, который должен вернуть список CSS-классов, которые будут добавлены к тэгу <tr>.

from django.contrib import admin

@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
    
    def get_row_classes(self, request, obj=None):
        if obj.name.startswith("M"):
            return ["table-success"]
        elif obj.name.startswith("P"):
            return ["table-info"]
        return []

Результат:

Стилизация inline-форм

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 []

Результат:

Меню

Меню в сайдбаре настраивается путем заполнения списка PAPER_MENU в settings.py.

PAPER_MENU = [
    dict(       # Пункт меню для главной страницы
        label=_("Dashboard"),
        url="admin:index",
        icon="fa fa-fw fa-lg fa-area-chart",
    ),
    dict(       # Пункт меню для приложения
        app="app",
        icon="fa fa-fw fa-lg fa-home",
        models=[
            "Tag",
            "Category",
            "SubCategory",
        ]
    ),
    "-",        # Разделитель
    "auth",     # Приложение    
    "sites",    # Приложение
]

Пункт меню может быть задан одним из четырех способов:

  • Имя приложения (app_label).
    Все модели выбранного приложения образуют подменю. Порядок моделей будет определен автоматически.
  • Путь к модели (app_label.model_name).
    Создаст пункт меню с заголовком, соответствующим названию модели и соответствующим URL.
  • Строка-разделитель ("-")
    Добавляет горизонтальную линию. С помощью разделителей можно визуально группировать пункты меню.
  • Словарь.
    Самый гибкий способ создания пункта меню. В словаре можно явным образом указать название пункта меню, его URL, иконку, CSS-классы и вложенные пункты.

При использовании словаря можно указать следующие ключи:

  • label: str - заголовок пункта меню
  • url: str - URL или имя URL-шаблона (например app:index)
  • icon: str - CSS-классы иконки
  • classes: str - CSS-классы пункта меню
  • perms: str/list/callable - права, необходимые для отображения пункта. Для определения суперюзера и сотрудников, можно использовать специальные значения superuser и staff.
  • app: str - имя приложения. Неявно добавляется к именам моделей в models.
  • models: list/dict - дочерние пункты меню. Может содержать имена моделей или пункты меню.

Пример 1.

from django.urls import reverse_lazy

PAPER_MENU = [
    dict(
      app="app",
      icon="fa fa-fw fa-lg fa-home",
      models=[
          dict(
              label=_("Index"),
              url=reverse_lazy("admin:app_list", kwargs={
                  "app_label": "app"
              })
          ),            # Произвольный пункт меню
          "Tag",        # Модель app.Tag
          "Category",   # Модель app.Category
      ]
    ),
    "-",        # Разделитель
    "auth",     # Приложение
]

Пример 2. Проверка прав.

Этот пункт меню увидят только сотрудники, имеющие право на изменение модели Tag.

На доступность страниц параметр perms никак не влияет. Если пользователь знает адрес страницы или ссылка на неё имеется где-то ещё, то пользователь сможет на неё перейти.

PAPER_MENU = [
    dict(
        app="app",
        perms=["staff", "app.change_tag"],
        models=[
            "Tag",
        ]
    )
]

Бейдж (badge)

Полоса с текстом в сайдбаре.

Её основное предназначение - визуально обозначить окружение, в котором работает административный интерфейс. Так вы не перепутаете сервер разработки с продашеном.

Цвет полосы и текст устанавливаются в settings.py:

PAPER_ENVIRONMENT_NAME = "development"
PAPER_ENVIRONMENT_COLOR = "#FFFF00"

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

paper-admin-3.0.1.tar.gz (3.4 MB view hashes)

Uploaded Source

Built Distribution

paper_admin-3.0.1-py2.py3-none-any.whl (3.5 MB view hashes)

Uploaded Python 2 Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page