Интерфейс администратора Django

Одна из сильных сторон Django – это автоматический интерфейс администратора. Он использует мета-данные модели чтобы предоставить многофункциональный, готовый к использованию интерфейс для работы с содержимым сайта. В этом разделе мы расскажем как установить, использовать и настроить интерфейс администратора Django.

Обзор

Интерфейс администратора по умолчанию включен, если вы создавали проект командой startproject.

Changed in Django 1.6:

In previous versions, the admin wasn’t enabled by default.

Если необходимо самостоятельно его включить, вот требования:

  1. Добавьте 'django.contrib.admin' в настройку INSTALLED_APPS.

  2. Интерфейс администратора имеет четыре зависимости - django.contrib.auth, django.contrib.contenttypes, django.contrib.messages and django.contrib.sessions. Если эти приложения не входят в список INSTALLED_APPS, добавьте их.

  3. Добавьте django.contrib.messages.context_processors.messages в TEMPLATE_CONTEXT_PROCESSORS и django.contrib.auth.middleware.AuthenticationMiddleware с MessageMiddleware в MIDDLEWARE_CLASSES. (Эти параметры включены по умолчанию, вам необходимо добавить их, только если вы изменяли эти настройки.)

  4. Определите, какие модели будут редактироваться через интерфейс администратора.

  5. Для каждой модели вы можете создать класс ModelAdmin, который инкапсулирует настройки интерфейса администратора для конкретной модели.

  6. Создайте экземпляр AdminSite и добавьте в него ваши модели с соответствующими классами ModelAdmin.

  7. Добавьте AdminSite в URLconf.

После всего это вы можете использовать интерфейс администратора Django посетив соответствующий URL (/admin/ по умолчанию).

Другие разделы

См.также

Информацию о настройке статических файлов (изображений, JavaScript и CSS) для интерфейса администратора на “боевом” сервере можно найти в разделе Обслуживание файлов.

Вопросы? Обратитесь к FAQ: Админка.

Объект ModelAdmin

class ModelAdmin

Класс ModelAdmin – это отображение модели в интерфейсе администратора. Его код добавляют в файл admin.py вашего приложения. Давайте рассмотрим простой пример ModelAdmin:

from django.contrib import admin
from myproject.myapp.models import Author

class AuthorAdmin(admin.ModelAdmin):
    pass
admin.site.register(Author, AuthorAdmin)

Do you need a ModelAdmin object at all?

В этом примере, класс ModelAdmin не определяет никаких настроек (пока).В результате мы получим интерфейс предоставляемый по умолчанию. Если он вас устраивает, вы можете не определять ModelAdmin совсем и зарегистрировать модель без ModelAdmin. Пример выше может выглядеть таким образом:

from django.contrib import admin
from myproject.myapp.models import Author

admin.site.register(Author)

Настройки ModelAdmin

ModelAdmin очень гибкий. Он содержит ряд параметров для настройки интерфейса администратора. Все настройки определяются в подклассе ModelAdmin:

from django.contrib import admin

class AuthorAdmin(admin.ModelAdmin):
    date_hierarchy = 'pub_date'
ModelAdmin.actions

Список действий, которые будут включены на странице списка объектов. Подробности смотрите в разделе Admin actions.

ModelAdmin.actions_on_top
ModelAdmin.actions_on_bottom

Определяет где на странице будет расположены панели с действиями. По умолчанию эта панель расположена сверху (actions_on_top = True; actions_on_bottom = False).

ModelAdmin.actions_selection_counter

Указывает отображать ли счетчик выбранных объектов после списка действий. По умолчанию он отображается (actions_selection_counter = True).

ModelAdmin.date_hierarchy

Укажите в date_hierarchy название DateField или DateTimeField поля вашей модели, и страница списка объектов будет содержать навигацию по датам из этого поля.

Например:

date_hierarchy = 'pub_date'

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

Примечание

date_hierarchy использует внутри QuerySet.datetimes(). Обратитесь к описанию, чтобы узнать некоторые нюансы при использовании часовых поясов (USE_TZ = True).

ModelAdmin.exclude

Этот атрибут должен содержать список полей, которые не будут включены в форму редактирования.

Например, у нас есть следующая модель:

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)
    title = models.CharField(max_length=3)
    birth_date = models.DateField(blank=True, null=True)

Если вам необходима форма для модели Author, которая содержит только поля name и title, вы можете определить параметр fields или exclude следующим образом:

from django.contrib import admin

class AuthorAdmin(admin.ModelAdmin):
    fields = ('name', 'title')

class AuthorAdmin(admin.ModelAdmin):
    exclude = ('birth_date',)

Так как модель содержит только три поля, name, title и birth_date, полученные формы будут содержать одинаковые поля.

ModelAdmin.fields

Если вам необходимо внести небольшие изменения форму на странице редактирования и добавления, например, изменить список отображаемых полей, их порядок или сгруппировать их, вы можете использовать настройку fields (сложные изменения можно выполнить используя настройку fieldsets описанную ниже). Например, необходимо изменить форму модели из приложения django.contrib.flatpages.models.FlatPage:

class FlatPageAdmin(admin.ModelAdmin):
    fields = ('url', 'title', 'content')

В примере выше будут отображаться только поля url, title и content. fields может содержать поля указанные в ModelAdmin.readonly_fields, они не будут доступны для редактирования.

Параметр fields, в отличии от list_display, может содержать только названия полей модели или полей определенных в form. Можно указать названия функций, если они указаны в readonly_fields.

Чтобы поля отображались в одной строке, укажите их в кортеже вместе. В этом примере, поля url и title будут отображаться в одном ряду, поле content будет расположено ниже:

class FlatPageAdmin(admin.ModelAdmin):
    fields = (('url', 'title'), 'content')

Note

Настройку fields не следует путать с ключом словаря fields в настройке fieldsets, описанной ниже.

Если не определен ни атрибут fields, ни fieldsets, Django покажет все поля с editable=True кроме AutoField, в одном наборе полей в порядке, в котором они указанные в модели.

ModelAdmin.fieldsets

Позволяет изменить макет страниц добавления и редактирования объекта.

fieldsets – это список двух-элементных кортежей, каждый представляет <fieldset> в форме редактирования объекта. (<fieldset> – группа полей в форме.)

Кортеж должен быть в формате (name, options полей), где name это название группы полей, а field_options – словарь с информацией о группе полей, включая список полей для отображения.

Полный пример для модели django.contrib.flatpages.models.FlatPage:

from django.contrib import admin

class FlatPageAdmin(admin.ModelAdmin):
    fieldsets = (
        (None, {
            'fields': ('url', 'title', 'content', 'sites')
        }),
        ('Advanced options', {
            'classes': ('collapse',),
            'fields': ('enable_comments', 'registration_required', 'template_name')
        }),
    )

Этот пример будет выглядеть следующим образом:

../../../_images/flatfiles_admin.png

Если не определен ни атрибут fields, ни fieldsets, Django покажет все поля с editable=True кроме AutoField, в одном наборе полей в порядке, в котором они указанные в модели.

Словарь field_options может содержать следующие ключи:

  • fields

    Кортеж с названиями полей. Этот ключ обязателен.

    Например:

    {
    'fields': ('first_name', 'last_name', 'address', 'city', 'state'),
    }
    

    Как и в атрибуте fields, чтобы отобразить поля в одной строке, добавьте их в один кортеж. В этом примере, поля first_name и last_name будут показаны в одной строке:

    {
    'fields': (('first_name', 'last_name'), 'address', 'city', 'state'),
    }
    

    fields может содержать значения из ModelAdmin.readonly_fields, чтобы отображать поля без возможности их редактирования.

    Добавление функции в fields аналогично добавлению в параметр fields - функция должна быть указанна в readonly_fields.

  • classes

    Список содержащий CSS классы, которые будут добавлены в группу полей.

    Например:

    {
    'classes': ('wide', 'extrapretty'),
    }
    

    Django предоставляет два класса для использования: collapse и wide. Группа полей с классом collapse будет показа в свернутом виде с кнопкой “развернуть”. Группа полей с классом wide будет шире по горизонтали.

  • description

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

    Заметим, что этот текст не будет экранирован. Это позволяет добавить вам HTML на страницу. Вы можете использовать обычный текст экранировав его функцией django.utils.html.escape().

ModelAdmin.filter_horizontal

По умолчанию, поле ManyToManyField отображается как <select multiple>. Однако, это поле тяжело использовать при большом количестве объектов. Добавив ManyToManyField в этот атрибут, будет использоваться “виджет” с JavaScript фильтром для поиска. Смотрите описание filter_vertical про использование вертикального “виджета”.

ModelAdmin.filter_vertical

Аналогичен filter_horizontal, но использует вертикальный “виджет”.

ModelAdmin.form

По умолчанию ModelForm создается динамически для модели. Этот атрибут используется для определения формы на страницах добавления и редактирования. Вы можете указать собственный подкласс ModelForm для переопределения этих страниц. Вы можете модифицировать форму, а не создавать с нуля свою, переопределив метод ModelAdmin.get_form().

Пример смотрите в разделе `Adding custom validation to the admin`_.

Note

Changed in Django 1.6.

Если вы указали атрибут Meta.model для ModelForm, необходимо также указать Meta.fields (или Meta.exclude). Однако, если поля указаны при определении настроек интерфейса администратора, атрибут Meta.fields будет проигнорирован.

Если ModelForm используется только для интерфейса администратора, проще всего не указывать атрибут Meta.model, т.к. ModelAdmin укажет правильную модель. Вы можете указать fields = [] в Meta чтобы ModelForm была правильной.

Note

Если и ModelForm и ModelAdmin определяют опцию exclude, ModelAdmin будет иметь больший приоритет:

from django import forms
from django.contrib import admin
from myapp.models import Person

class PersonForm(forms.ModelForm):

    class Meta:
        model = Person
        exclude = ['name']

class PersonAdmin(admin.ModelAdmin):
    exclude = ['age']
    form = PersonForm

В этом примере, поле “age” не будет добавлено в форму в отличии от поля “name”.

ModelAdmin.formfield_overrides

Позволяет быстро изменить настройки отображения различных типов Field в интерфейсе администратора. formfield_overrides – словарь указывающий параметры для классов полей, которые будут передаваться в конструкторы указанных полей.

Все это звучит немного абстрактно, так что давайте рассмотрим пример. Самое распространенное применение formfield_overrides это переопределить “виджет” поля формы. Предположим у нас есть RichTextEditorWidget, который использует расширенное поля редактирования вместо <textarea>. Вот как мы может использовать его:

from django.db import models
from django.contrib import admin

# Import our custom widget and our model from where they're defined
from myapp.widgets import RichTextEditorWidget
from myapp.models import MyModel

class MyModelAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.TextField: {'widget': RichTextEditorWidget},
    }

Заметим что ключ словаря класс поля, а не строка. Значение это словарь с аргументами. Это аргументы будут переданы в __init__(). Подробности смотрите в разделе API форм.

Предупреждение

Если вы хотите использовать собственный “виджет” для полей внешних ключей (например, ForeignKey или ManyToManyField), убедитесь что поле не добавлено в raw_id_fields или radio_fields.

formfield_overrides не позволяет переопределить “виджет” для полей из raw_id_fields или radio_fields, потому что raw_id_fields и radio_fields переопределяют виджет.

ModelAdmin.inlines

Смотрите описание ниже InlineModelAdmin, а также ModelAdmin.get_formsets().

ModelAdmin.list_display

list_display указывает какие поля отображать на странице списка объектов.

Например:

list_display = ('first_name', 'last_name')

Если list_display не указан, Django отобразить только результат __unicode__()``(``__str__() для Python 3) объекта.

Вы можете указать четыре варианта значений в list_display:

  • Поле модели. Например:

    class PersonAdmin(admin.ModelAdmin):
        list_display = ('first_name', 'last_name')
    
  • Функция, которая принимает один аргумент - объект модели. Например:

    def upper_case_name(obj):
        return ("%s %s" % (obj.first_name, obj.last_name)).upper()
    upper_case_name.short_description = 'Name'
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = (upper_case_name,)
    
  • Название атрибута ModelAdmin. Работает так же, как и функция. Например:

    class PersonAdmin(admin.ModelAdmin):
        list_display = ('upper_case_name',)
    
        def upper_case_name(self, obj):
            return ("%s %s" % (obj.first_name, obj.last_name)).upper()
        upper_case_name.short_description = 'Name'
    
  • Название атрибута модели. Работает так же, как и функция, но self в этом контексте объект модели. Например:

    from django.db import models
    from django.contrib import admin
    
    class Person(models.Model):
        name = models.CharField(max_length=50)
        birthday = models.DateField()
    
        def decade_born_in(self):
            return self.birthday.strftime('%Y')[:3] + "0's"
        decade_born_in.short_description = 'Birth decade'
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ('name', 'decade_born_in')
    

Несколько особенностей list_display:

  • Если указано поле ForeignKey, Django покажет результат __unicode__()``(``__str__() для Python 3) связанного объекта.

  • ManyToManyField не поддерживается, так как это влечет к созданию SQL запроса для каждого объекта. Если вам необходимо сделать это в любом случае, создайте метод модели и используйте его в list_display. (Смотрите ниже подробности про использование методов в list_display.)

  • Если поле BooleanField или NullBooleanField, Django покажет красивую “on” или “off” иконку вместо True или False.

  • Если используется метод модели, ModelAdmin или функция, Django по умолчанию экранирует результат. Если вам не нужно экранировать результат, добавьте атрибут allow_tags методу или функции со значением True. Однако, для избежания XSS атак, следует использовать format_html() для значений передаваемых пользователем.

    Пример:

    from django.db import models
    from django.contrib import admin
    from django.utils.html import format_html
    
    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        last_name = models.CharField(max_length=50)
        color_code = models.CharField(max_length=6)
    
        def colored_name(self):
            return format_html('<span style="color: #{0};">{1} {2}</span>',
                               self.color_code,
                               self.first_name,
                               self.last_name)
    
        colored_name.allow_tags = True
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ('first_name', 'last_name', 'colored_name')
    
  • Если используется метод модели, ModelAdmin или функция, которая возвращает True или False, Django покажет “on” или “off” иконки, если вы добавите атрибут boolean методу или функции со значением True.

    Пример:

    from django.db import models
    from django.contrib import admin
    
    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        birthday = models.DateField()
    
        def born_in_fifties(self):
            return self.birthday.strftime('%Y')[:3] == '195'
        born_in_fifties.boolean = True
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ('name', 'born_in_fifties')
    
  • Вы можете использовать методы __str__() и ``__unicode__()`:

    list_display = ('__unicode__', 'some_other_field')
    
  • Обычно элементы list_display, которые не являются полями модели, не могу быть использованы при сортировке (так как Django выполняет сортировку на уровне базы данных).

    Однако, если элемент list_display представляет определенное поле в базе данных, вы можете указать это добавив атрибут admin_order_field к элементу.

    Например:

    from django.db import models
    from django.contrib import admin
    from django.utils.html import format_html
    
    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        color_code = models.CharField(max_length=6)
    
        def colored_first_name(self):
            return format_html('<span style="color: #{0};">{1}</span>',
                               self.color_code,
                               self.first_name)
    
        colored_first_name.allow_tags = True
        colored_first_name.admin_order_field = 'first_name'
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ('first_name', 'colored_first_name')
    

    В этом примере Django будет использовать поле first_name при сортировке по colored_first_name.

  • В list_display можно указать свойства. Однако, учитывая как свойства работают в Python, параметр short_description свойства можно указать используя функцию property(), а не декоратор @property.

    Например:

    class Person(object):
        first_name = models.CharField(max_length=50)
        last_name = models.CharField(max_length=50)
    
        def my_property(self):
            return self.first_name + ' ' + self.last_name
        my_property.short_description = "Full name of the person"
    
        full_name = property(my_property)
    
    class PersonAdmin(admin.ModelAdmin):
        list_display = ('full_name',)
    
  • New in Django 1.6.

    Названия полей из list_display будут использованы в CSS классах, форма будет содержать класс column-<field_name> для каждого элемента <th>. Так можно определить ширину полей через CSS стили.

Используйте list_display_links, чтобы указать какие поля в list_display будут ссылками на страницу редактирования объекта.

По умолчанию, на страницу редактирования объекта будет вести ссылка в первой колонке. Но list_display_links позволяет изменить это поведение. Установите list_display_links в список или кортеж полей (аналогично list_display).

list_display_links может указывать одно или несколько полей. Пока указанные поля входят в list_display, Django безразлично сколько их. Единственное требование: для использования list_display_links вы должны указать list_display.

В этом примере поля first_name и last_name будут отображаться как ссылки на страницу редактирования объекта:

class PersonAdmin(admin.ModelAdmin):
    list_display = ('first_name', 'last_name', 'birthday')
    list_display_links = ('first_name', 'last_name')
ModelAdmin.list_editable

Укажите в list_editable список полей, которые можно будет редактировать на странице списка объектов. То есть, поля из list_editable будут отображаться как поля формы позволяя пользователям изменять значения и сохранять изменения для всех строк сразу.

Примечание

list_editable взаимодействует с другими настройками следующим образом:

  • Поле из list_editable должно входить в list_display. Вы не можете редактировать поле, которое не отображается!

  • Поле не может быть в list_editable и list_display_links вместе – поле не может быть ссылкой и полем формы.

Вы получите ошибку проверки если нарушите эти правила.

ModelAdmin.list_filter

Укажите list_filter, чтобы определить фильтры данных в правой панели страницы списка объектов, как показано на изображении:

../../../_images/users_changelist.png

list_filter - это список элементов, которые могу быть одного из следующих типов:

  • название поля следующего типа: BooleanField, CharField, DateField, DateTimeField, IntegerField, ForeignKey или ManyToManyField. Например:

    class PersonAdmin(admin.ModelAdmin):
        list_filter = ('is_staff', 'company')
    

    Поле в list_filter может указывать и на связанный объект используя __, например:

    class PersonAdmin(admin.UserAdmin):
        list_filter = ('company__name',)
    
  • подкласс django.contrib.admin.SimpleListFilter, в котором необходимо определить атрибуты title и parameter_name и переопределить методы lookups и queryset, например:

    from datetime import date
    
    from django.contrib import admin
    from django.utils.translation import ugettext_lazy as _
    
    class DecadeBornListFilter(admin.SimpleListFilter):
        # Human-readable title which will be displayed in the
        # right admin sidebar just above the filter options.
        title = _('decade born')
    
        # Parameter for the filter that will be used in the URL query.
        parameter_name = 'decade'
    
        def lookups(self, request, model_admin):
            """
            Returns a list of tuples. The first element in each
            tuple is the coded value for the option that will
            appear in the URL query. The second element is the
            human-readable name for the option that will appear
            in the right sidebar.
            """
            return (
                ('80s', _('in the eighties')),
                ('90s', _('in the nineties')),
            )
    
        def queryset(self, request, queryset):
            """
            Returns the filtered queryset based on the value
            provided in the query string and retrievable via
            `self.value()`.
            """
            # Compare the requested value (either '80s' or '90s')
            # to decide how to filter the queryset.
            if self.value() == '80s':
                return queryset.filter(birthday__gte=date(1980, 1, 1),
                                        birthday__lte=date(1989, 12, 31))
            if self.value() == '90s':
                return queryset.filter(birthday__gte=date(1990, 1, 1),
                                        birthday__lte=date(1999, 12, 31))
    
    class PersonAdmin(admin.ModelAdmin):
        list_filter = (DecadeBornListFilter,)
    

    Примечание

    Для удобства объект HttpRequest передается в методы lookups и queryset, например:

    class AuthDecadeBornListFilter(DecadeBornListFilter):
    
        def lookups(self, request, model_admin):
            if request.user.is_superuser:
                return super(AuthDecadeBornListFilter,
                    self).lookups(request, model_admin)
    
        def queryset(self, request, queryset):
            if request.user.is_superuser:
                return super(AuthDecadeBornListFilter,
                    self).queryset(request, queryset)
    

    Также объект ModelAdmin передается в метод lookups. Например, вы можете использовать существующие данные при создании фильтра:

    class AdvancedDecadeBornListFilter(DecadeBornListFilter):
    
        def lookups(self, request, model_admin):
            """
            Only show the lookups if there actually is
            anyone born in the corresponding decades.
            """
            qs = model_admin.get_queryset(request)
            if qs.filter(birthday__gte=date(1980, 1, 1),
                          birthday__lte=date(1989, 12, 31)).exists():
                yield ('80s', _('in the eighties'))
            if qs.filter(birthday__gte=date(1990, 1, 1),
                          birthday__lte=date(1999, 12, 31)).exists():
                yield ('90s', _('in the nineties'))
    
  • кортеж, где первый элемент название поля, а второй - подкласс django.contrib.admin.FieldListFilter, например:

    class PersonAdmin(admin.ModelAdmin):
        list_filter = (
            ('is_staff', admin.BooleanFieldListFilter),
        )
    

    Примечание

    API FieldListFilter считается внутренним и может быть изменено.

Вы можете указать собственный шаблон для отображения фильтра:

class FilterWithCustomTemplate(admin.SimpleListFilter):
    template = "custom_template.html"

Конкретный пример можно найти в шаблоне приложения интерфейса администратора, который использует Django (admin/filter.html).

ModelAdmin.list_max_show_all

Используйте list_max_show_all, чтобы указать количество объектов на странице списка объектов при выборе “Показать все”. Интерфейс администратора покажет ссылку “Показать все” только если общее количество объектов меньше или равно этому значению. По умолчанию равно 200.

ModelAdmin.list_per_page

Используйте list_per_page, чтобы определить количество объектов на одной странице при отображении списка объектов. По умолчанию равно 100.

Используйте list_select_related, чтобы указать Django использовать select_related() при выборе объектов для страницы отображения объектов. Это может сократить количество запросов на этой странице.

Changed in Django dev.

Значение должно быть булево, список или кортеж. По умолчанию равно False.

При True, select_related() всегда будет использоваться. При False, Django найдет в list_display все ForeignKey и будет использовать их с select_related().

Если вам нужен больший контроль, используйте список или кортеж для list_select_related. Пустой кортеж укажет не использовать select_related. Не пустой кортеж будет передан как параметр для select_related. Например:

class ArticleAdmin(admin.ModelAdmin):
    list_select_related = ('author', 'category')

вызовет select_related('author', 'category').

ModelAdmin.ordering

ordering позволяет определить сортировку на странице списка объектов. Это должен быть список или кортеж в формате аналогичном параметру ordering.

Если атрибут не указана, Django будет использовать сортировку по умолчанию модели.

Если вам необходима динамическая сортировка (например, в зависимости от пользователя или текущего языка) вы можете определить метод get_ordering().

ModelAdmin.paginator

Класс, используемый для создания постраничного отображения. По умолчанию используется django.core.paginator.Paginator. Если конструктор вашего класса принимает параметры отличные от django.core.paginator.Paginator, вам необходимо также переопределить метод ModelAdmin.get_paginator().

ModelAdmin.prepopulated_fields

prepopulated_fields позволяет определить поля, которые получают значение основываясь на значениях других полей:

class ArticleAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("title",)}

Указанные поля будут использовать код JavaScript для заполнения поля значением на основе значений полей-источников. Основное применение - это генерировать значение для полей SlugField из значений другого поля или полей. Процесс генерирования состоит в объединении значений полей-источников и преобразованию результата в правильный “slug” (например, заменой пробелов на дефисы).

prepopulated_fields не принимает поля DateTimeField, ForeignKey или ManyToManyField.

ModelAdmin.preserve_filters
New in Django 1.6.

Интерфейс администратора сохраняет состояние фильтров после создания, редактирования и удаления объектов. Чтобы отключить это поведение, используйте False.

ModelAdmin.radio_fields

По умолчанию Django использует <select> для полей ForeignKey или тех, которые содержат choices. Ели поле указанно в radio_fields, Django будет использовать радио кнопки. Предположим что group поле ForeignKey в модели Person:

class PersonAdmin(admin.ModelAdmin):
    radio_fields = {"group": admin.VERTICAL}

Вы можете использовать HORIZONTAL или VERTICAL из модуля django.contrib.admin.

Не добавляйте в radio_fields поля, которые не являются ForeignKey или не содержат choices.

ModelAdmin.raw_id_fields

По умолчанию Django использует <select> для полей ForeignKey. Если связанных объектов очень много, создание <select> может быть очень затратным процессом.

raw_id_fields содержит список полей, которые будут использовать поле Input для ForeignKey или ManyToManyField:

class ArticleAdmin(admin.ModelAdmin):
    raw_id_fields = ("newspaper",)

Виджет поля для raw_id_fields будет содержать значение первичного ключа для ForeignKey или список ключей для ManyToManyField. Возле поля есть кнопка поиска и выбора связанных объектов:

../../../_images/raw_id_fields.png
ModelAdmin.readonly_fields

По умолчанию интерфейс администратора отображает все поля как редактируемые. Поля указанные в этой настройке (которая является list или tuple) будут отображаться значение без возможности редактировать, они также будут исключены из ModelForm используемой для создания и редактирования объектов. Однако, если вы определяете аргумент ModelAdmin.fields или ModelAdmin.fieldsets поля для чтения должны быть в них указаны (иначе они будут проигнорированы).

Если readonly_fields используется без определения порядка полей через атрибуты ModelAdmin.fields или ModelAdmin.fieldsets, поля из этой настройки будут отображаться после редактируемых полей.

Read-only поле может показывать данные не только поля модели, но и метода, а также метода определенного в подклассе ModelAdmin. Работает как ModelAdmin.list_display. Это позволяет отображать различную информацию о редактируемом объекте, например:

from django.contrib import admin
from django.utils.html import format_html_join
from django.utils.safestring import mark_safe

class PersonAdmin(admin.ModelAdmin):
    readonly_fields = ('address_report',)

    def address_report(self, instance):
        # assuming get_full_address() returns a list of strings
        # for each line of the address and you want to separate each
        # line by a linebreak
        return format_html_join(
            mark_safe('<br/>'),
            '{0}',
            ((line,) for line in instance.get_full_address()),
        ) or "<span class='errors'>I can't determine this address.</span>"

    # short_description functions like a model field's verbose_name
    address_report.short_description = "Address"
    # in this example, we have used HTML tags in the output
    address_report.allow_tags = True
ModelAdmin.save_as

Укажите save_as, чтобы включить возможность “сохранять как” на странице редактирования объекта.

По умолчанию страница содержит три кнопки: “Сохранить”, “Сохранить и продолжить редактирование” и “Сохранить и добавить новый”. Если save_as равен True, “Сохранить и добавить новый” будет заменена кнопкой “Сохранить как”.

“Сохранить как” сохранит объект как новый (с новым ID).

По умолчанию save_as равен False.

ModelAdmin.save_on_top

Укажите save_on_top, чтобы добавить кнопки сохранения в верхней части страницы редактирования объекта.

По умолчанию кнопки сохранения отображаются под формой. Если указать save_on_top, кнопки будут отображаться и сверху и снизу.

По умолчанию save_on_top равен False.

ModelAdmin.search_fields

search_fields позволяет добавить поиск на страницу списка объектов. Этот атрибут должен содержать список полей, которые будут использоваться при поиске.

Эти поля должны быть текстовыми, таким как CharField или TextField. Вы можете указать поля из связанных объектов используя __:

search_fields = ['foreign_key__related_fieldname']

Например, у нас есть модель записи в блоге с полем автора. Следующая настройка позволит искать записи по email адресу автора:

search_fields = ['user__email']

При поиске Django разбивает поисковый запрос на слова и возвращает объекты, которые содержат эти слова в одном из указанных в search_fields полей. Поиск регистро-независимый. Например, если search_fields равен ['first_name', 'last_name'] и пользователь выполняет поиск по john lennon, Django создаст такое SQL условие WHERE:

WHERE (first_name ILIKE '%john%' OR last_name ILIKE '%john%')
AND (first_name ILIKE '%lennon%' OR last_name ILIKE '%lennon%')

Для определения более конкретных условий поиска используйте следующие префиксы:

^

Указывает на начало строки. Например, если search_fields установить в ['^first_name', '^last_name'] и пользователь ищет john lennon, Django создаст следующее SQL условие WHERE:

WHERE (first_name ILIKE 'john%' OR last_name ILIKE 'john%')
AND (first_name ILIKE 'lennon%' OR last_name ILIKE 'lennon%')

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

=

Полное совпадение, регистро-независимое. Например, если search_fields равно ['=first_name', '=last_name'] и пользователь ищет john lennon, Django создаст следующее SQL условие WHERE:

WHERE (first_name ILIKE 'john' OR last_name ILIKE 'john')
AND (first_name ILIKE 'lennon' OR last_name ILIKE 'lennon')

Поисковый запрос разбивается по пробелам, по этому, исходя из примера выше, нельзя найти записи с полем first_name равным ``‘john winston’``(содержащим пробел).

@

Выполняет полнотекстовый поиск. Работает как и обычный поиск, но использует индекс. На данный момент это работает только в MySQL.

Вы можете переопределить метод ModelAdmin.get_search_results(), чтобы указать дополнительные параметры при поиске, или переопределить механизм поиска.

Настройки шаблонов

Раздел `Overriding Admin Templates`_ описывает как переопределить или расширить шаблоны интерфейса администратора. Используйте следующие настройки, чтобы переопределить шаблоны, которые используются представлениями ModelAdmin:

ModelAdmin.add_form_template

Путь к шаблону, который используется add_view().

ModelAdmin.change_form_template

Путь к шаблону, который используется change_view().

ModelAdmin.change_list_template

Путь к шаблону, который используется changelist_view().

ModelAdmin.delete_confirmation_template

Путь к шаблону, который используется delete_view() для отображения страницы подтверждения удаления одного или нескольких объектов.

ModelAdmin.delete_selected_confirmation_template

Путь к шаблону, который используется delete_selected для отображения страницы подтверждения удаления одного или нескольких объектов. Подробности смотрите в разделе о действиях в интерфейсе администратора.

ModelAdmin.object_history_template

Путь к шаблону, который используется history_view().

Методы ModelAdmin

Предупреждение

Методы ModelAdmin.save_model() и ModelAdmin.delete_model() должны сохранять/удалять объект. Их задача выполнять дополнительные операции, а не разрешать/запрещать операции удаления/сохранения.

ModelAdmin.save_model(self, request, obj, form, change)

Метод save_model принимает объект HttpRequest, экземпляр модели, экземпляр ModelForm и булево значение указывающее создан объект или изменяется. В этом методе вы может выполнить дополнительные операции до или после сохранения.

Например, добавление request.user к объекту перед сохранением объекта:

from django.contrib import admin

class ArticleAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        obj.user = request.user
        obj.save()
ModelAdmin.delete_model(self, request, obj)

Метод delete_model принимает объект HttpRequest и экземпляр модели, который удаляется. В этом методе вы может выполнить дополнительные операции до или после удаления.

ModelAdmin.save_formset(self, request, form, formset, change)

Метод save_formset принимает объект HttpRequest, ModelForm родительского объекта, “formset” связанных объектов и булево значение указывающее создан родительский объект или изменяется.

Например, добавление request.user к каждому объекту измененному в “formset”:

class ArticleAdmin(admin.ModelAdmin):
    def save_formset(self, request, form, formset, change):
        instances = formset.save(commit=False)
        for instance in instances:
            instance.user = request.user
            instance.save()
        formset.save_m2m()
ModelAdmin.get_ordering(self, request)

Метод get_ordering принимает объект request и должен вернуть list или tuple с параметрами сортировки аналогично атрибуту ordering. Например:

class PersonAdmin(admin.ModelAdmin):

    def get_ordering(self, request):
        if request.user.is_superuser:
            return ['name', 'rank']
        else:
            return ['name']
ModelAdmin.get_search_results(self, request, queryset, search_term)
New in Django 1.6.

Метод get_search_results фильтрует список объектов в соответствии с параметрами поиска. Он принимает запрос, queryset объектов, и параметры поиска указанные пользователем. Возвращает кортеж содержащий полученный queryset и булево указывающее может ли результат содержать одинаковые значения.

По умолчанию выполняется поиск по полям из ModelAdmin.search_fields.

Вы можете переопределить это метод поменяв механизм поиска. Например, вы можете искать по численным полям, или использовать поисковый движок, такой как Solr или Haystack. Вы должны определять наличие дубликатов в результате поиска и возвращать True вторым элементом результата.

Например, для поиска по численным полям можно сделать:

class PersonAdmin(admin.ModelAdmin):
    list_display = ('name', 'age')
    search_fields = ('name',)

    def get_search_results(self, request, queryset, search_term):
        queryset, use_distinct = super(PersonAdmin, self).get_search_results(request, queryset, search_term)
        try:
            search_term_as_int = int(search_term)
            queryset |= self.model.objects.filter(age=search_term_as_int)
        except:
            pass
        return queryset, use_distinct

Метод save_related принимает объект HttpRequest, родительскую форму ModelForm, список “inline formsets” и булево значение указывающее создан родительский объект или изменяется. Вы можете выполнить дополнительные операции перед и после сохранения объектов. Заметим, что к этому моменту родительский объект и его формы уже будут сохранены.

ModelAdmin.get_readonly_fields(self, request, obj=None)

Метод get_readonly_fields принимает объект HttpRequest и редактируемый объект obj (или None для формы создания нового объекта) и должен вернуть list или tuple содержащий список полей, которые будут отображаться только для чтения как описано в разделе ModelAdmin.readonly_fields.

ModelAdmin.get_prepopulated_fields(self, request, obj=None)

Метод get_prepopulated_fields принимает объект HttpRequest и редактируемый объект obj (или None для формы создания нового объекта) и должен вернуть словарь полей аналогичный ModelAdmin.prepopulated_fields.

ModelAdmin.get_list_display(self, request)

Метод get_list_display принимает объект HttpRequest и редактируемый объект obj (или None для формы создания нового объекта) и должен вернуть list или tuple содержащий список полей, которые будут отображаться на странице списка объектов как описано в разделе ModelAdmin.list_display.

Метод get_list_display_links принимает объект HttpRequest и list или tuple указанный в ModelAdmin.get_list_display(). Должен вернуть list или tuple содержащий список полей, который будут ссылками на странице списка объектов к странице редактирования. Смотрите описание ModelAdmin.list_display_links.

ModelAdmin.get_fieldsets(self, request, obj=None)

Метод get_fieldsets принимает объект HttpRequest и редактируемый объект obj (или None для формы создания нового объекта) и должен вернуть список двухэлеменых кортежей, которые определяют <fieldset> формы, как было описано выше в разделе про ModelAdmin.fieldsets.

ModelAdmin.get_list_filter(self, request)
New in Django 1.5.

Метод get_list_filter принимает HttpRequest должен вернуть значение формата, соответствующего параметру list_filter.

ModelAdmin.get_inline_instances(self, request, obj=None)
New in Django 1.5.

Метод get_inline_instances принимает объект HttpRequest и редактируемый объект obj (или None для формы создания нового объекта) и должен вернуть list или tuple содержащий объекты InlineModelAdmin, как описано в разделе о InlineModelAdmin.

ModelAdmin.get_urls(self)

Метод get_urls класса ModelAdmin возвращает URL-ы, которые используются для ModelAdmin в URLconf. Вы можете дополнить их как описано в Менеджер URL-ов:

class MyModelAdmin(admin.ModelAdmin):
    def get_urls(self):
        urls = super(MyModelAdmin, self).get_urls()
        my_urls = patterns('',
            (r'^my_view/$', self.my_view)
        )
        return my_urls + urls

    def my_view(self, request):
        # custom view which should return an HttpResponse
        pass

Примечание

Заметим, что собственные URL-шаблоны включаются перед URL-шаблонами интерфейса администратора, которые удовлетворяют почти всем URL-ам. По этому желательно добавлять собственные URL-шаблоны перед встроенными.

В этом примере, представление my_view будет доступно по ссылке /admin/myapp/mymodel/my_view/ (предполагается что вы добавили URL-ы интерфейса администратора к /admin/.)

Однако, есть несколько проблем, связанных с функцией self.my_view:

  • Не выполняются никакие проверки прав, каждый может выполнить запрос к этому представлению.

  • Не отправляются заголовки управляющие кэшированием. Это означает, если включен кэширующий функциональный слой, данные из базы данных на странице могут быть неверными.

Обычно это не то, что вам нужно, Django предоставляет функцию-обертку для проверки прав и отключения кэширования. Эта функция – AdminSite.admin_view() (или self.admin_site.admin_view в методе ModelAdmin). Используйте ее следующим образом:

class MyModelAdmin(admin.ModelAdmin):
    def get_urls(self):
        urls = super(MyModelAdmin, self).get_urls()
        my_urls = patterns('',
            (r'^my_view/$', self.admin_site.admin_view(self.my_view))
        )
        return my_urls + urls

Обратите внимание на следующую строку:

(r'^my_view/$', self.admin_site.admin_view(self.my_view))

Функция-обертка защищает self.my_view от несанкционированного доступа и применяет декоратор django.views.decorators.cache.never_cache для предотвращения кэширования.

Если страницу необходимо кэшировать, но необходимо проверять авторизацию, вы можете использовать аргумент cacheable=True для AdminSite.admin_view:

(r'^my_view/$', self.admin_site.admin_view(self.my_view, cacheable=True))
ModelAdmin.get_form(self, request, obj=None, **kwargs)

Возвращает класс ModelForm используемый при добавлении и редактировании объектов, смотрите описание add_view() и change_view().

Если вы хотите скрыть некоторые поля для не суперпользователей, можно переопределить метод get_form:

class MyModelAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        self.exclude = []
        if not request.user.is_superuser:
            self.exclude.append('field_to_hide')
        return super(MyModelAdmin, self).get_form(request, obj, **kwargs)
ModelAdmin.get_formsets(self, request, obj=None)

Выполняет yield по InlineModelAdmin, чтобы вернуть набор форм.

Например, если некоторый inline необходимо отображать только при редактировании, можно переопределить метод get_formsets:

class MyModelAdmin(admin.ModelAdmin):
    inlines = [MyInline, SomeOtherInline]

    def get_formsets(self, request, obj=None):
        for inline in self.get_inline_instances(request, obj):
            # hide MyInline in the add view
            if isinstance(inline, MyInline) and obj is None:
                continue
            yield inline.get_formset(request, obj)
ModelAdmin.formfield_for_foreignkey(self, db_field, request, **kwargs)

Метод formfield_for_foreignkey позволяет вам переопределить поле для внешнего ключа. Например, изменить выбор объектов в зависимости от пользователя:

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "car":
            kwargs["queryset"] = Car.objects.filter(owner=request.user)
        return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

Объект HttpRequest используется, чтобы отфильтровать для выбора объекты модели Car по текущему пользователю.

ModelAdmin.formfield_for_manytomany(self, db_field, request, **kwargs)

Как и метод formfield_for_foreignkey, formfield_for_manytomany позволяет переопределить поле формы для связей многое-ко-многим. Например, если пользователь может владеть несколькими машинами и машина может принадлежать нескольким пользователям, вы можете отфильтровать модель Car, чтобы отображать машины только текущего пользователя:

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_manytomany(self, db_field, request, **kwargs):
        if db_field.name == "cars":
            kwargs["queryset"] = Car.objects.filter(owner=request.user)
        return super(MyModelAdmin, self).formfield_for_manytomany(db_field, request, **kwargs)
ModelAdmin.formfield_for_choice_field(self, db_field, request, **kwargs)

Как и методы formfield_for_foreignkey и formfield_for_manytomany, formfield_for_choice_field позволяет переопределить поле формы для поля модели, которое содержит choices. Например, если главному администратору необходимо отображать варианты значений отличные от вариантов ответов для остальных пользователей:

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_choice_field(self, db_field, request, **kwargs):
        if db_field.name == "status":
            kwargs['choices'] = (
                ('accepted', 'Accepted'),
                ('denied', 'Denied'),
            )
            if request.user.is_superuser:
                kwargs['choices'] += (('ready', 'Ready for deployment'),)
        return super(MyModelAdmin, self).formfield_for_choice_field(db_field, request, **kwargs)
ModelAdmin.get_changelist(self, request, **kwargs)

Возвращает класс Changelist, который используется для отображения списка объекта. По умолчанию, используется django.contrib.admin.views.main.ChangeList. Унаследовав этот класс вы можете переопределить поведение этой страницы.

ModelAdmin.get_changelist_form(self, request, **kwargs)

Возвращает подкласс ModelForm для Formset, который будет использоваться на странице списка объектов. Например:

from django import forms

class MyForm(forms.ModelForm):
    pass

class MyModelAdmin(admin.ModelAdmin):
    def get_changelist_form(self, request, **kwargs):
        return MyForm

Note

Changed in Django 1.6.

Meta.model

ModelAdmin.get_changelist_formset(self, request, **kwargs)

Возвращает ModelFormSet, который будет использоваться на странице списка объектов, если включен list_editable. Например:

from django.forms.models import BaseModelFormSet

class MyAdminFormSet(BaseModelFormSet):
    pass

class MyModelAdmin(admin.ModelAdmin):
    def get_changelist_formset(self, request, **kwargs):
        kwargs['formset'] = MyAdminFormSet
        return super(MyModelAdmin, self).get_changelist_formset(request, **kwargs)
ModelAdmin.has_add_permission(self, request)

Должен возвращать True, если пользователю позволено добавлять новый объект, иначе False.

ModelAdmin.has_change_permission(self, request, obj=None)

Должен возвращать True, если пользователю позволено изменять объект, иначе False. Если obj равен None, должен вернуть True или False, указывая может ли пользователь изменить какой-либо объект данного типа.

ModelAdmin.has_delete_permission(self, request, obj=None)

Должен вернуть True, если пользователю позволено удалять объект, иначе False. Если obj равен None, должен вернуть True или False, указывая может ли пользователь удалить какой-либо объект данного типа.

ModelAdmin.get_queryset(self, request)

Метод get_queryset возвращает QuerySet всех объектов модели, которые можно редактировать в интерфейсе администратора. Этот метод можно использовать для отображения объектов принадлежащих текущему пользователю:

class MyModelAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super(MyModelAdmin, self).get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(author=request.user)
Changed in Django 1.6:

The get_queryset method was previously named queryset.

ModelAdmin.message_user(request, message, level=messages.INFO, extra_tags='', fail_silently=False)

Отправляет сообщение пользователю используя django.contrib.messages. Смотрите пример переопределения ModelAdmin.

Changed in Django 1.5:

Keyword arguments were added in Django 1.5.

Аргументы позволят изменить тип сообщения, добавить дополнительные CSS теги, или указать игнорировать ошибку, если contrib.messages не установлен. Эти аргументы совпадают с аргументами django.contrib.messages.add_message(). Единственное отличие - тип сообщения можно указать строкой, а не только числом или константой.

ModelAdmin.get_paginator(queryset, per_page, orphans=0, allow_empty_first_page=True)

Возвращает объект для постраничного отображения. По умолчанию возвращает объект paginator.

Остальные методы

ModelAdmin.add_view(self, request, form_url='', extra_context=None)

Представление Django для страницы добавления объекта модели. Смотрите описание ниже.

ModelAdmin.change_view(self, request, object_id, form_url='', extra_context=None)

Представление Django для страницы редактирования объекта модели. Смотрите описание ниже.

ModelAdmin.changelist_view(self, request, extra_context=None)

Представление Django для страницы отображения всех объектов модели. Смотрите описание ниже.

ModelAdmin.delete_view(self, request, object_id, extra_context=None)

Представление Django для страницы подтверждения удаления объектов. Смотрите описание ниже.

ModelAdmin.history_view(self, request, object_id, extra_context=None)

Представление Django для страницы истории изменений объекта модели.

В отличии от методов ModelAdmin описанных выше, которые позволяют изменять поведение интерфейса администратора, эти пять методов используются как представления Django для выполнения CRUD-операций над объектами модели. В результате, полностью переопределив эти методы можно радикально изменить интерфейс администратора.

Одна из причин переопределить эти методы – добавить данные в контекст шаблона. В этом примере представление для изменения объекта добавляет дополнительные данные в контекст, чтобы отобразить их в шаблоне:

class MyModelAdmin(admin.ModelAdmin):

    # A template for a very customized change view:
    change_form_template = 'admin/myapp/extras/openstreetmap_change_form.html'

    def get_osm_info(self):
        # ...
        pass

    def change_view(self, request, object_id, form_url='', extra_context=None):
        extra_context = extra_context or {}
        extra_context['osm_data'] = self.get_osm_info()
        return super(MyModelAdmin, self).change_view(request, object_id,
            form_url, extra_context=extra_context)

Эти представления возвращают объект TemplateResponse, что позволяет легко изменить данные ответа перед выполнением шаблона. Подробности смотрите в разделе о TemplateResponse.

Добавление статических файлов в ModelAdmin

В некоторых ситуациях вам может понадобиться добавить CSS и/или JavaScript файлы в представления добавления или изменения объектов. Вы можете выполнить это добавив класс Media в ModelAdmin:

class ArticleAdmin(admin.ModelAdmin):
    class Media:
        css = {
            "all": ("my_styles.css",)
        }
        js = ("my_code.js",)

Приложение staticfiles добавляет STATIC_URL (или MEDIA_URL если STATIC_URL равно None) к указанным путям. Это же правильно применяется и к определению статических файлов для форм.

jQuery

Javascript интерфейса администратора использует библиотеку jQuery.

Чтобы избежать конфликтов с библиотеками пользователя, jQuery в Django(версия 1.9.1) используется пространство имен django.jQuery. Если вы хотите использовать jQuery в собственных JavaScript файлах без добавления еще одной копии, вы можете использовать объект django.jQuery на страницах списка объектов и добавления/редактирования.

Changed in Django 1.6:

The embedded jQuery has been upgraded from 1.4.2 to 1.9.1.

Класс ModelAdmin использует jQuery по умолчанию, так что вам не нужно добавлять jQuery в список media-файлов ModelAdmin. Если вам необходима библиотека jQuery в глобальном пространстве имен, например при использовании плагинов jQuery, или более новая версия jQuery, вам необходимо добавить собственную копию jQuery.

Django содержит сжатую и ‘minified’ версию jQuery, как jquery.js и jquery.min.js соответственно.

ModelAdmin и InlineModelAdmin содержат свойство media, которое возвращает список объектов Media, которые содержат путь к JavaScript файлам для форм и наборов форм. Если DEBUG равна True, будет использована несжатая версия jquery.js, иначе сжатая версия.

Дополнительная проверка данных в интерфейсе администратора

Добавить дополнительную проверку данных в интерфейсе администратора очень легко. Интерфейс администратора использует django.forms, и класс ModelAdmin позволяет указать вашу собственную форму:

class ArticleAdmin(admin.ModelAdmin):
    form = MyArticleAdminForm

Вы можете определить форму MyArticleAdminForm где угодно, просто импортируйте ее. В собственной форме вы можете добавить дополнительную проверку данных для любого поля:

class MyArticleAdminForm(forms.ModelForm):
    def clean_name(self):
        # do something that validates your data
        return self.cleaned_data["name"]

Важно использовать ModelForm, чтобы избежать проблем и ошибок. Подробности смотрите в документации о формах раздел про проверку полей и, особенно, раздел о переопределении метода clean() в ModelForm.

Объект InlineModelAdmin

class InlineModelAdmin
class TabularInline
class StackedInline

Интерфейс администратора позволяет редактировать связанные объекты на одной странице с родительским объектом. Это называется “inlines”. Например, у нас есть две модели:

from django.db import models

class Author(models.Model):
   name = models.CharField(max_length=100)

class Book(models.Model):
   author = models.ForeignKey(Author)
   title = models.CharField(max_length=100)

Вы можете редактировать книги автора на странице редактирования автора. Вы добавляете “inlines” к модели добавив их в ModelAdmin.inlines:

from django.contrib import admin

class BookInline(admin.TabularInline):
    model = Book

class AuthorAdmin(admin.ModelAdmin):
    inlines = [
        BookInline,
    ]

Django предоставляет два подкласса InlineModelAdmin:

Разница между ними только в используемом шаблоне.

Параметры InlineModelAdmin

InlineModelAdmin содержит некоторые возможности ModelAdmin и собственные. Общие методы и атрибуты определены в классе BaseModelAdmin, вот они:

Параметры класса InlineModelAdmin:

InlineModelAdmin.model

Модель используемая в “inline”. Обязательный параметр.

InlineModelAdmin.fk_name

Название внешнего ключа модели. В большинстве случаев он определяется автоматически, но вы должны указать fk_name, если модель содержит несколько внешних ключей к родительской модели.

InlineModelAdmin.formset

По умолчанию – BaseInlineFormSet. Использование собственного класса предоставляет большие возможности для переопределения поведения по умолчанию. Смотрите раздел о наборах модельных форм.

InlineModelAdmin.form

Значение form по умолчанию – ModelForm. Это значение передается в inlineformset_factory() при создании набора форм.

InlineModelAdmin.extra

Указывает количество пустых форм для добавления объектов в наборе форм. Подробности смотрите в разделе о наборе форм.

Если JavaScript включен в браузере, ссылка “Add another” позволит добавить новую пустую форму в дополнение к формам указанным параметром extra.

Ссылка не появится если количество отображаемых форм превышает значение в параметре max_num, или если у пользователя отключен JavaScript.

New in Django 1.6.

InlineModelAdmin.get_extra() позволяет указать количество дополнительных форм.

InlineModelAdmin.max_num

Указывает максимальное количество форм. Этот параметр не определяет количество связанных объектов. Подробности смотрите в разделе Ограничение количества редактируемых объектов.

New in Django 1.6.

InlineModelAdmin.get_max_num() позволяет указать максимальное количество дополнительных форм.

InlineModelAdmin.raw_id_fields

По умолчанию Django использует <select> для полей ForeignKey. Если связанных объектов очень много, создание <select> может быть очень затратным процессом.

raw_id_fields – список полей которые должны использовать Input виджет для полей ForeignKey или ManyToManyField:

class BookInline(admin.TabularInline):
    model = Book
    raw_id_fields = ("pages",)
InlineModelAdmin.template

Шаблон для отображения.

InlineModelAdmin.verbose_name

Позволяет переопределить значение verbose_name класса Meta модели.

InlineModelAdmin.verbose_name_plural

Позволяет переопределить значение verbose_name_plural класса Meta модели.

InlineModelAdmin.can_delete

Определяет можно ли удалять связанные объекты. По умолчанию равно True.

InlineModelAdmin.get_formset(self, request, obj=None, **kwargs)

Возвращает BaseInlineFormSet. Смотрите ModelAdmin.get_formsets.

InlineModelAdmin.get_extra(self, request, obj=None, **kwargs)
New in Django 1.6.

Возвращает количество форм. По умолчанию возвращает значение атрибута InlineModelAdmin.extra.

Вы можете переопределить метод и добавить логику для определения количества форм. Например, учитывать данные объекта модели(передается как именованный аргумент obj):

class BinaryTreeAdmin(admin.TabularInline):
    model = BinaryTree

    def get_extra(self, request, obj=None, **kwargs):
        extra = 2
        if obj:
            return extra - obj.binarytree_set.count()
        return extra
InlineModelAdmin.get_max_num(self, request, obj=None, **kwargs)
New in Django 1.6.

Возвращает максимальное количество дополнительных форм. По умолчанию возвращает значение атрибута InlineModelAdmin.max_num.

Вы можете переопределить метод и добавить логику для определения максимального количества форм. Например, учитывать данные объекта модели(передается как именованный аргумент obj):

class BinaryTreeAdmin(admin.TabularInline):
    model = BinaryTree

    def get_max_num(self, request, obj=None, **kwargs):
        max_num = 10
        if obj.parent:
            return max_num - 5
        return max_num

Работа с моделью с несколькими внешними ключами к одной модели

Модель может содержать несколько внешних ключей к одной модели, например:

from django.db import models

class Friendship(models.Model):
    to_person = models.ForeignKey(Person, related_name="friends")
    from_person = models.ForeignKey(Person, related_name="from_friends")

Если вы хотите использовать “inline” для этой модели на странице редактирования/добавления объектов Person, вам необходимо указать какой внешний ключ использовать:

from django.contrib import admin
from myapp.models import Friendship

class FriendshipInline(admin.TabularInline):
    model = Friendship
    fk_name = "to_person"

class PersonAdmin(admin.ModelAdmin):
    inlines = [
        FriendshipInline,
    ]

Работа со связями много-ко-многим

По умолчанию, для полей ManyToManyField используется виджет определенный интерфейсом администратора. В зависимости от параметров ModelAdmin``может использоваться стандартное HTML поле ``<select multiple>, горизонтальный или вертикальный фильтр, или виджет raw_id_admin. Однако вместо этого можно использовать “inline”.

Предположим у нас есть следующие модели:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, related_name='groups')

Необходимо определить объект InlineModelAdmin для связи многое-ко-многим:

from django.contrib import admin

class MembershipInline(admin.TabularInline):
    model = Group.members.through

class PersonAdmin(admin.ModelAdmin):
    inlines = [
        MembershipInline,
    ]

class GroupAdmin(admin.ModelAdmin):
    inlines = [
        MembershipInline,
    ]
    exclude = ('members',)

Есть две вещи в этом примере, которые следует отметить.

Первая - класс MembershipInline ссылается на Group.members.through. Атрибут through указывает на модель управляющую связью многое-ко-многим. Эта модель автоматически создается Django при определении связи.

Вторая – класс GroupAdmin должен явно исключить поле members. Django отображает поле для связи многое-ко-многим (в нашем случае с моделью Group). Если вы хотите использовать “inline”, необходимо указать Django, что поле не нужно отображать - иначе мы получим два виджета для редактирования связи.

Во всем остальном InlineModelAdmin работает так же, как и всегда. Вы можете управлять отображением используя параметры ModelAdmin.

Работа с промежуточной моделью связи многое-ко-многим

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

Однако, нам необходимо редактировать информацию промежуточной модели. К счастью это можно сделать используя “inline” в интерфейсе администратора. Предположим у нас есть следующие модели:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

class Membership(models.Model):
    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

Для начала определим отображение модели Membership в интерфейсе администратора:

class MembershipInline(admin.TabularInline):
    model = Membership
    extra = 1

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

Теперь определим отображение моделей Person и Group:

class PersonAdmin(admin.ModelAdmin):
    inlines = (MembershipInline,)

class GroupAdmin(admin.ModelAdmin):
    inlines = (MembershipInline,)

Теперь зарегистрируем модели Person и Group в интерфейсе администратора:

admin.site.register(Person, PersonAdmin)
admin.site.register(Group, GroupAdmin)

Теперь вы можете редактировать объекты Membership на странице объектов Person и Group.

Использование связей с несколькими моделями в интерфейсе администратора

Вы можете использовать “inline” для связей с несколькими моделями(generic relations). Предположим, у вас есть следующие модели:

from django.db import models

class Image(models.Model):
    image = models.ImageField(upload_to="images")
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey("content_type", "object_id")

class Product(models.Model):
    name = models.CharField(max_length=100)

Если вы хотите редактировать и добавлять объекты Image на странице добавления/редактирования объектов Product, вы можете использовать GenericTabularInline или GenericStackedInline (подклассы GenericInlineModelAdmin) из модуля django.contrib.contenttypes.generic. Они отображают группы форм для добавления и редактирования связанных объектов, как и аналогичные классы из приложения интерфейса администратора. Пример admin.py для наших моделей:

from django.contrib import admin
from django.contrib.contenttypes import generic

from myproject.myapp.models import Image, Product

class ImageInline(generic.GenericTabularInline):
    model = Image

class ProductAdmin(admin.ModelAdmin):
    inlines = [
        ImageInline,
    ]

admin.site.register(Product, ProductAdmin)

Подробности смотрите в разделе о contenttypes.

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

Переопределить шаблоны, которые использует интерфейс администратора, очень легко. Вы можете переопределить шаблон для конкретного приложения или модели.

Настройка каталогов в шаблонами

Шаблоны интерфейса администратора находятся в каталоге contrib/admin/templates/admin.

Чтобы переопределить их, создайте каталог admin в каталоге templates вашего проекта. Это может быть любой другой каталог указанный в настройке TEMPLATE_DIRS.

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

Чтобы переопределить шаблон для определенного приложения, скопируйте и отредактируйте необходимый шаблон из каталога django/contrib/admin/templates/admin и сохраните его в созданном подкаталоге.

Например, если необходимо изменить шаблон для представления списка объектов для всех моделей в приложении my_app, скопируйте contrib/admin/templates/admin/change_list.html в каталог templates/admin/my_app/ проекта и выполните необходимые изменения.

Если необходимо изменить шаблон только для модели ‘Page’, скопируйте тот же файл в каталог templates/admin/my_app/page проекта.

Переопределение или замена шаблона в интерфейсе администратора

Учитывая модульную структуру шаблонов в интерфейсе администратора, как правило нет необходимости заменять весь шаблон. Целесообразней переопределить только необходимый блок шаблона.

Продолжим пример выше. Например, необходимо добавить ссылку после ссылки History для модели Page. Изучив change_form.html можно увидеть, что нам необходимо переопределить только блок object-tools-items. Вот наш новый шаблон change_form.html :

{% extends "admin/change_form.html" %}
{% load i18n admin_urls %}
{% block object-tools-items %}
    <li>
        <a href="{% url opts|admin_urlname:'history' original.pk|admin_urlquote %}" class="historylink">{% trans "History" %}</a>
    </li>
    <li>
        <a href="mylink/" class="historylink">My Link</a>
    </li>
    {% if has_absolute_url %}
        <li>
            <a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% trans "View on site" %}</a>
        </li>
    {% endif%}
{% endblock %}

Вот и все! Добавим шаблон в каталог templates/admin/my_app и ссылка появится на странице редактирования объекта для всех моделей приложения my_app.

Шаблоны, которые можно переопределить для приложения или модели

Не каждый шаблон в contrib/admin/templates/admin можно переопределить для приложения или модели. Вот список переопределяемых шаблонов:

  • app_index.html
  • change_form.html
  • change_list.html
  • delete_confirmation.html
  • object_history.html

Остальные шаблоны вы можете все еще переопределить для всего проекта. Просто добавьте новую версию шаблона в каталог templates/admin. Это особенно полезно для переопределения страниц для 404 и 500 ошибки.

Примечание

Некоторые шаблоны, такие как change_list_request.html используются для отображения включаемых тегов(inclusion tags). Вы можете переопределить их, но лучше создать собственную версию тега, которая будет использовать новый шаблон. В этом случае вы сможете использовать оба шаблона.

Главный шаблон и шаблон страницы входа

Чтобы переопределить шаблоны главной страницы и страниц входа/выхода, лучше создать собственный экземпляр AdminSite (смотрите ниже), и изменить свойства AdminSite.index_template , AdminSite.login_template и AdminSite.logout_template.

Объект AdminSite

class AdminSite(name='admin')

Интерфейс администратора Django представлен экземпляром django.contrib.admin.sites.AdminSite. По умолчанию, экземпляр этого класса находится в django.contrib.admin.site и вы можете зарегистрировать модели с подклассами ModelAdmin в нем.

Если вам необходимо изменить поведение интерфейса администратора, вы можете создать подкласс AdminSite и переопределить все что вам нужно. Затем создайте экземпляр вашего подкласса AdminSite (как и любого другого класса Python), и зарегистрируйте в нем ваши модели.

При создании экземпляра AdminSite, вы можете указать уникальное название экземпляра приложения передав аргумент name в конструктор. Это название используется для идентификации экземпляра что важно при поиске URL-ов интерфейса администратора. Если этот аргумент не указан, будет использовано значение по умолчанию admin.

Атрибуты AdminSite

Можно переопределить или заменить основные шаблоны в интерфейсе администратора как это описано в разделе `Overriding Admin Templates`_.

AdminSite.index_template

Шаблон, который будет использоваться для главной страницы.

AdminSite.app_index_template

Шаблон, который будет использоваться для главной страницой приложения.

AdminSite.login_template

Шаблон, который будет использоваться для страницы входа.

AdminSite.login_form

Подкласс AuthenticationForm который будет использовать для представления авторизации в интерфейсе администратора.

AdminSite.logout_template

Шаблон, который будет использоваться для страницы выхода.

AdminSite.password_change_template

Шаблон, который будет использоваться для страницы смены пароля.

AdminSite.password_change_done_template

Шаблон, который будет использоваться для страницы завершения смены пароля.

Добавление экземпляра AdminSite в URLconf

Последний шаг это добавить ваш экземпляр AdminSite в URLconf. Выполните это добавив в URLconf свойство AdminSite.urls.

В этом примере мы добавляем экземпляр по умолчанию AdminSite, который находится в django.contrib.admin.site, для URL /admin/

# urls.py
from django.conf.urls import patterns, include
from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns('',
    (r'^admin/', include(admin.site.urls)),
)

Перед этим мы используем admin.autodiscover() для загрузки модулей admin.py для приложений в INSTALLED_APPS.

В этом примере мы добавляем собственный экземпляр AdminSite, который находится в myproject.admin.admin_site для URL /myadmin/

# urls.py
from django.conf.urls import patterns, include
from myproject.admin import admin_site

urlpatterns = patterns('',
    (r'^myadmin/', include(admin_site.urls)),
)

Нет необходимости использовать autodiscover если вы импортируете все модули admin.py в myproject.admin для регистрации моделей.

Несколько интерфейсов администратора в одном URLconf

Django позволяет легко создать несколько интерфейсов администратора для одного сайта. Просто создайте несколько экземпляров AdminSite и добавьте его к различным URL-ам.

В этом примере, URL-ы /basic-admin/ и /advanced-admin/ ведут к различным экземплярам AdminSitemyproject.admin.basic_site и myproject.admin.advanced_site соответственно:

# urls.py
from django.conf.urls import patterns, include
from myproject.admin import basic_site, advanced_site

urlpatterns = patterns('',
    (r'^basic-admin/', include(basic_site.urls)),
    (r'^advanced-admin/', include(advanced_site.urls)),
)

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

Добавление представлений в интерфейс администратора

Как и ModelAdmin AdminSite содержит метод get_urls(), который можно переопределить и добавить собственные представления в интерфейс администратора. Переопределите метод get_urls() и добавьте URL-шаблоны с вашими представлениями.

Примечание

Каждое представление, которое использует шаблоны интерфейса администратора или унаследованные шаблоны, должно указать аргумент current_app для конструктора RequestContext или Context при выполнении шаблона. Он должен быть равен self.name, если представление определенно в AdminSite, или self.admin_site.name, если определено в ModelAdmin.

Добавление возможности сброса пароля

Вы можете определить возможно сбросить пароль добавив несколько строк в URLconf. А точнее добавьте эти четыре URL-шаблона:

url(r'^admin/password_reset/$', 'django.contrib.auth.views.password_reset', name='admin_password_reset'),
url(r'^admin/password_reset/done/$', 'django.contrib.auth.views.password_reset_done', name='password_reset_done'),
url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$', 'django.contrib.auth.views.password_reset_confirm', name='password_reset_confirm'),
url(r'^reset/done/$', 'django.contrib.auth.views.password_reset_complete', name='password_reset_complete'),
Changed in Django 1.6:

The pattern for password_reset_confirm() changed as the uid is now base 64 encoded.

(Предполагается что вы уже добавили приложения интерфейса администратора к admin/ и что оно указано после всех URL-шаблонов начинающихся с ^admin/ ).

Наличие URL-шаблона с названием admin_password_reset приведет к появлению ссылки “забыли ваш пароль?” на странице входа в интерфейсе администратора.

Поиск URL-ов интерфейса администратора

После установки AdminSite можно указывать ссылки к представлениям интерфейса администратора используя систему поиска URL-ов.

AdminSite предоставляет следующие именованные URL-шаблоны:

Страница

Название URL-а

Параметры

Главная

index  

Выхода

logout  

Смена пароля

password_change  

Завершения смены пароля

password_change_done  
i18n javascript jsi18n  

Главная страница приложения

app_list app_label

Редирект на страницу объекта

view_on_site content_type_id, object_id

Каждый экземпляр ModelAdmin предоставляет дополнительные именованные URL-шаблоны:

Страница

Название URL-а

Параметры

Список объектов

{{ app_label }}_{{ model_name }}_changelist  

Добавления объекта

{{ app_label }}_{{ model_name }}_add  

Истории

{{ app_label }}_{{ model_name }}_history object_id

Удаления объекта

{{ app_label }}_{{ model_name }}_delete object_id

Изменения объекта

{{ app_label }}_{{ model_name }}_change object_id

Эти именованные URL-шаблоны зарегистрированы с названием экземпляра приложения admin и названием экземпляра приложения, указанным при создании AdminSite.

Например, нам необходимо получить ссылку на страницу изменения объекта модели Choice (из приложения polls) в стандартном интерфейсе администратора. Для этого необходимо выполнить следующий код:

>>> from django.core import urlresolvers
>>> c = Choice.objects.get(...)
>>> change_url = urlresolvers.reverse('admin:polls_choice_change', args=(c.id,))

Этот код найдет первый зарегистрированный экземпляр приложения admin (не зависимо от определенного названия экземпляра) и найдет ссылку на представление для редактирования объекта poll.Choice.

Если необходимо найти URL в определенном экземпляре приложения, укажите значение current_app. Например, если необходимо получить ссылку на представление в экземпляре приложения с названием custom, выполните следующий код:

>>> change_url = urlresolvers.reverse('custom:polls_choice_change', args=(c.id,))

Подробности смотрите в разделе о пространстве имен в конфигурации URL-ов.

Для более удобного поиска URL-ов в шаблонах, Django предоставляет фильтр admin_urlname, который принимает название действия в качестве аргумента:

{% load admin_urls %}
<a href="{% url opts|admin_urlname:'add' %}">Add user</a>
<a href="{% url opts|admin_urlname:'delete' user.pk %}">Delete this user</a>

Название действия совпадает с последней частью названия URL-шаблона в ModelAdmin, которые описаны выше. Значение opts может быть любым объектом содержащим app_label и model_name, эта переменная обычно указывается представлением в интерфейсе администратора для текущей модели.