Как использовать пользовательский класс AdminSite? - PullRequest
26 голосов
/ 02 февраля 2011

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

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

Я исправил это с помощью небольшого взлома.Я написал этот класс:

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.

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

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

Ответы [ 3 ]

23 голосов
/ 05 мая 2015

Проблема

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

Решение

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

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

  2. Два варианта:

    1. 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
      
    2. 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, использовать его с некоторыми опасностямиэти версии.См. Примечания ниже.

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

Примечания и предостережения

  • Приложение, которое выполняет переключение, должно действительно быть приложением first в списке 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 и без проблем использовал метод конфигурации приложения.

6 голосов
/ 04 февраля 2011

Цитирование из 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.

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

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

4 голосов
/ 24 июля 2018

В Django 2.1 есть готовое решение: https://docs.djangoproject.com/en/2.1/ref/contrib/admin/#overriding-the-default-admin-site

from django.contrib import admin

class MyAdminSite(admin.AdminSite):
...

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

from django.contrib.admin.apps import AdminConfig

class MyAdminConfig(AdminConfig):
    default_site = 'myproject.admin.MyAdminSite'


INSTALLED_APPS = [
    ...
    'myproject.apps.MyAdminConfig',  # replaces 'django.contrib.admin'
    ...
]

Обратите внимание на разницу между AdminConfig и SimpleAdminConfig, где последний не вызывает admin.autodiscover(). В настоящее время я использую это решение в проекте.

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