Как использовать пользовательский класс AdminSite?

Каков наилучший способ реализовать собственный django.contrib.admin.sites.AdminSite?

На самом деле у меня возникла проблема с регистрацией INSTALLED_APPS в django.contrib.admin.autodiscover. Если я использую свой собственный класс AdminSite в urls.py, на странице администратора не было приложений.

Я исправил это с помощью litte hack. Я написал этот класс:

from django.contrib.admin.sites import site as default_site

class AdminSiteRegistryFix( object ):
    '''
    This fix links the '_registry' property to the orginal AdminSites
    '_registry' property. This is necessary, because of the character of
    the admins 'autodiscover' function. Otherwise the admin site will say,
    that you havn't permission to edit anything.
    '''

    def _registry_getter(self):
        return default_site._registry

    def _registry_setter(self,value):
        default_site._registry = value

    _registry = property(_registry_getter, _registry_setter)

И реализовать свой пользовательский AdminSite следующим образом:

from wltrweb.hacks.django.admin import AdminSiteRegistryFix
from django.contrib.admin import AdminSite

class MyAdminSite( AdminSite, AdminSiteRegistryFix ):
    # do some magic
    pass        


site = MyAdminSite()

Поэтому я могу использовать этот site для urls.py.

Кто-нибудь знает лучший способ? Поскольку я обращаюсь к var, начинающему с подчеркивания, это не более чем хак. Мне не нравятся хаки.

Изменить: Другой способ - переписать функцию django.contrib.admin.autodiscover, но в этом случае у меня будет избыточный код.

Ответ 1

Цитата из https://docs.djangoproject.com/en/1.10/ref/contrib/admin/#customizing-the-adminsite-class

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

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

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

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

Итак, если вы не хотите работать с хаком выше, я просто вижу эти два варианта. Замените все вызовы регистратора на свой собственный сайт администратора или зарегистрируйте модели явно в своем модуле adminsite.

Ответ 2

Проблема

Использование пользовательского класса, полученного из django.contrib.admin.AdminSite для сайта администратора проекта, без необходимости писать собственный регистрационный код для регистрации моделей с новым классом. Когда я использую сторонние приложения со своими собственными моделями, я бы предпочел не редактировать собственный регистрационный код только потому, что модели были добавлены или удалены из этих приложений.

Решение

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

  • Наличие приложения, которое будет выполнять коммутатор. (Я использую свое проектное приложение с именем core для моих собственных целей.)

  • Два варианта:

    • Django 1.6 - 1.9: используйте __init__ приложения для выполнения этого переключателя. В Django 1.8 вы получите предупреждение об устаревании из-за изменения в Django 1.9, приведенном ниже. Обратите внимание, что этот метод будет работать с 1.9, так как модули Django, загруженные приведенным ниже кодом, были изменены в 1.9, чтобы они больше не загружали модели. Когда я использую этот метод, мой файл core/__init__.py содержит:

      from django.contrib import admin
      from django.contrib.admin import sites
      
      class MyAdminSite(admin.AdminSite):
          pass
      
      mysite = MyAdminSite()
      admin.site = mysite
      sites.site = mysite
      
    • Django 1.9 и более: используйте конфигурацию приложения приложения для выполнения этого переключателя. Начиная с Django 1.9, в качестве примечания выпуска:

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

      Я предпочитаю ограничивать импорт, который я делаю на корневом уровне, чтобы избежать риска загрузки моделей. Хотя с версии 1.9 с использованием метода __init__ выше будет работать, не сообщается, если 1.10 или более поздняя версия введет изменение, которое вызовет проблемы.

      Когда я использую этот метод, core/__init__.py устанавливает default_app_config = "core.apps.DefaultAppConfig", и у меня есть core/apps.py, как это:

      from django.apps import AppConfig
      
      class DefaultAppConfig(AppConfig):
          name = 'core'
      
          def ready(self):
              from django.contrib import admin
              from django.contrib.admin import sites
      
              class MyAdminSite(admin.AdminSite):
                  pass
      
              mysite = MyAdminSite()
              admin.site = mysite
              sites.site = mysite
      

      Хотя этот метод можно использовать с версиями 1.7 и 1.8, немного рискованно использовать его с этими версиями. См. Примечания ниже.

  • Размещение этого приложения ранее, чем django.contrib.admin в списке INSTALLED_APPS. (Это абсолютно необходимо для 1.7 и более поздних версий. В более ранних версиях Django это может сработать, даже если приложение позже django.contrib.admin. Однако см. Примечания ниже.)

Заметки и предостережения

  • Приложение, которое выполняет переключатель, должно быть действительно первым приложением в списке INSTALLED_APPS, чтобы свести к минимуму вероятность того, что что-то еще захватит значение site от django.contrib.admin до того, как будет выполнен переход, Если другому приложению удастся получить это значение до того, как коммутатор будет завершен, тогда другое приложение будет иметь ссылку на старый сайт. Веселость, несомненно, наступит.

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

  • Возможно, что будущий выпуск Django может нарушить этот метод.

  • Для версии до 1.9 я предпочел использовать __init__ для переключения сайта с помощью конфигурации приложения, поскольку документация при инициализации указывает, что ready() метод конфигураций приложений называется относительно поздним. Между тем, когда загружен модуль приложения и вызывается время ready(), модели загружены, и в некоторых случаях это может означать, что модуль схватил значение site от django.contrib.admin до ready называется. Чтобы свести к минимуму риск, у меня есть код приложения __init__.

    Я считаю, что риск, существовавший в версиях 1.7 и 1.8, и который я избегал, используя __init__ для выполнения переключения сайта как можно раньше, не существует в 1.9. Всем запрещается загружать модули до загрузки всех приложений. Поэтому включение переключателя в обратном вызове ready первого приложения, указанного в INSTALLED_APPS, должно быть безопасным. Я модернизировал большой проект до 1.9 и использовал метод настройки приложения без каких-либо проблем.