如何使用自定义AdminSite类?

sve*_*ltr 27 python django django-admin

哪个是实现我自己的最佳方式django.contrib.admin.sites.AdminSite

其实我得到一个问题登记INSTALLED_APPSdjango.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)
Run Code Online (Sandbox Code Playgroud)

并实现我的自定义AdminSite,如下所示:

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

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


site = MyAdminSite()
Run Code Online (Sandbox Code Playgroud)

所以,我可以用这个siteurls.py.

谁知道更好的方法?因为我以下划线开头访问var,所以它只不过是一个hack.我不喜欢黑客.

编辑:另一种方法是重写django.contrib.admin.autodiscover函数,但在这种情况下,我将有冗余代码.

Lou*_*uis 23

问题

使用从django.contrib.admin.AdminSite项目的管理站点派生的自定义类,而无需编写自定义注册代码来向新类注册模型.当我使用自己的模型使用第三方应用时,我宁愿不必编辑自定义注册码,因为模型是从这些应用中添加或删除的.

解决方案

你必须选择与用于管理站点到自己的实例的默认类,用自己的类创建创建实例之前 django.contrib.adminautodiscover函数被调用.我是这样做的:

  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
      
      Run Code Online (Sandbox Code Playgroud)
    2. Django 1.9及以上:使用应用程序的应用程序配置来执行切换.从Django 1.9开始,发布说明如下:

      所有模型都需要在已安装的应用程序中定义,或者声明一个显式的app_label.此外,在加载应用程序之前无法导入它们.特别是,无法在应用程序的根包中导入模型.

      我更喜欢限制我在根级别执行的导入,以避免加载模型的风险.虽然从使用上述__init__方法的1.9版开始工作,但无法确定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
      
      Run Code Online (Sandbox Code Playgroud)

      虽然可以在版本1.7和1.8中使用此方法,但将它与这些版本一起使用会有点冒险.请参阅下面的注释.

  3. 比此列表更早放置此应用程序.(这对于1.7及更高版本来说是绝对必要的.在Django的早期版本中,即使应用程序晚于.它也可能正常工作.但是,请参阅下面的注释.)django.contrib.adminINSTALLED_APPSdjango.contrib.admin

笔记和警告

  • 执行交换机的应用程序确实应该是第一个应用程序的INSTALLED_APPS列表,以尽量减少别的东西会抢的价值的机会site,从django.contrib.admin开关之前作出.如果另一个应用程序在切换完成之前设法获得该值,则该其他应用程序将具有对旧网站的引用.希拉里肯定会随之而来.

  • 如果两个应用程序试图安装自己的新默认管理站点类,上述方法将无法正常工作.这必须根据具体情况处理.

  • 未来的Django版本可能会破坏这种方法.

  • 对于1.9之前的版本,我更喜欢使用__init__app配置进行站点切换,因为初始化文档表明ready()app配置的方法相对较晚.时间之间的应用程序的模块被加载,时间ready()被调用时,模型已经加载,并且在某些情况下,这可能意味着一个模块抓住的值sitedjango.contrib.admin 之前 ready被调用.为了最大限度地降低风险,我让应用程序的__init__代码进行切换.

    我相信版本1.7和1.8中存在的风险以及我通过__init__尽可能早地执行站点切换而避免的风险在1.9中不存在.在加载所有应用程序之前,禁止每个人加载模块.因此,在ready列出的第一个应用程序的回调中进行切换INSTALLED_APPS应该是安全的.我已经将一个大项目升级到1.9并使用了app配置方法,没有任何问题.

  • 关于兼容性的注意事项,我正在使用你的方法与Django 1.9.4,并像一个魅力. (4认同)

Rei*_*cke 6

引自https://docs.djangoproject.com/en/1.10/ref/contrib/admin/#customizing-the-adminsite-class

但是,如果您想使用自定义行为设置自己的管理站点,则可以自由地将AdminSite子类化并覆盖或添加您喜欢的任何内容.然后,只需创建AdminSite子类的实例(与实例化任何其他Python类的方式相同),并使用它注册模型和ModelAdmin子类,而不是使用默认值.

我想这是最明确的方法,但它也意味着您需要更改应用程序admin.py文件中的注册码.

在使用您自己的AdminSite实例时,实际上不需要使用自动发现,因为您可能会导入myproject.admin模块中的所有每个应用程序的admin.py模块.

假设似乎是,一旦您开始编写自定义管理站点,它就会变得非常具体项目,并且您事先知道要包含哪些应用程序.

所以,如果你不想使用上面的hack,我真的只看到这两个选项.将所有注册调用替换为自定义管理站点,或在管理模块中明确注册模型.


Elw*_*win 6

从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):
...
Run Code Online (Sandbox Code Playgroud)

现在,通过将自己的AdminConfig添加到已安装的应用程序来完成自定义管理站点的交换。

from django.contrib.admin.apps import AdminConfig

class MyAdminConfig(AdminConfig):
    default_site = 'myproject.admin.MyAdminSite'
Run Code Online (Sandbox Code Playgroud)


INSTALLED_APPS = [
    ...
    'myproject.apps.MyAdminConfig',  # replaces 'django.contrib.admin'
    ...
]
Run Code Online (Sandbox Code Playgroud)

注意AdminConfig和SimpleAdminConfig之间的区别,后者后者不会触发admin.autodiscover()。我目前正在项目中使用此解决方案。

  • 使用此配置时,我遇到错误:导入错误:模块“myproject.admin”未定义“MyAdminSite”属性/类。任何想法? (5认同)
  • 当您像这样交换管理站点时,建议不要导入自己的管理站点来注册模型。只需使用django.contrib.admin导入站点和site.register(MyModel,MyModelAdmin)即可。 (2认同)

小智 5

在 Django 3.2 中实现站点范围的自定义 AdminSite 时遇到了同样的问题,我找到了一个解决方法来使它工作。

似乎应该相应地更新文档。

from django.contrib.admin.apps import AdminConfig

class MyAdminConfig(AdminConfig):
    default_site = 'myproject.admin.MyAdminSite'
Run Code Online (Sandbox Code Playgroud)

这引发了一个异常:

RuntimeError: 'myproject.apps' declares more than one default AppConfig: 'AdminConfig', 'MyAdminConfig'.

它是通过导入django.contrib.admin.apps而不是解决django.contrib.admin.apps.AdminConfig

from django.contrib.admin import apps

class MyAdminConfig(apps.AdminConfig):
    default_site = 'myproject.admin.MyAdminSite'
Run Code Online (Sandbox Code Playgroud)

然后异常继续:

django.core.exceptions.ImproperlyConfigured: Application labels aren't unique, duplicates: admin

这是由settings.py配置引起的:

INSTALLED_APPS = [
    'myproject.apps.MyAdminConfig',  #replaces django.contrib.admin
   ...
Run Code Online (Sandbox Code Playgroud)

它可以通过从设置中删除“MyAdminConfig”来解决:

INSTALLED_APPS = [
    'myproject.apps', #replaces django.contrib.admin
   ...
Run Code Online (Sandbox Code Playgroud)