Как использовать пользовательский класс 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 и использовал метод настройки приложения без каких-либо проблем.