如何使用自定义AdminSite类?

时间:2011-02-02 16:57:08

标签: python django django-admin

哪种方法是实现我自己的django.contrib.admin.sites.AdminSite

实际上我在INSTALLED_APPS中注册django.contrib.admin.autodiscover时出现问题。如果我在urls.py中使用自定义AdminSite类,则管理页面上不会显示任何应用。

我用一个小黑客修复了这个问题。我写了这堂课:

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,所以它只不过是一个hack。我不喜欢黑客。

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

4 个答案:

答案 0 :(得分:23)

问题

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

解决方案

您必须将使用管理网站使用的默认类创建的实例切换到您自己的实例,使用您自己的类创建 django.contrib.admin' s {{1函数被调用。我是这样做的:

  1. 拥有可执行切换的应用。 (我出于自己的目的使用名为autodiscover的项目专用应用程序。)

  2. 两种选择:

    1. Django 1.6到1.9:使用应用的core 来执行切换。在Django 1.8中,由于下面引用的Django 1.9的更改,您将收到弃用警告。请注意,此方法 也适用于1.9,因为下面显示的代码加载的Django模块已在1.9中更改,因此它们不再加载模型。当我使用此方法时,我的__init__文件包含:

      core/__init__.py
    2. Django 1.9及以上版本:使用应用的应用配置来执行切换。从Django 1.9开始,作为release notes state

        

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

      我更喜欢限制我在根级别执行的导入,以避免加载模型的风险。从版本1.9开始,使用上面的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.10或更高版本是否会引入会导致问题的更改。

      当我使用此方法时,__init__设置core/__init__.py并且我有default_app_config = "core.apps.DefaultAppConfig"这样:

      core/apps.py

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

  3. 将此应用提前放在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 列表中的django.contrib.admin。 (这对于1.7及更高版本来说是绝对必要的。在早期版本的Django中,即使应用程序晚于INSTALLED_APPS也可能正常工作。但是,请参阅下面的注释。)

  4. 注释和注意事项

    • 执行切换的应用程序应该是django.contrib.admin列表中的第一个应用程序,以便最大限度地减少其他内容获取{{1}的值的可能性在切换之前从INSTALLED_APPS开始。如果另一个应用程序设法在切换完成之前获得该值,则该其他应用程序将具有对旧网站的引用。 Hilarity肯定会随之而来。

    • 如果两个应用尝试安装自己的新默认管理网站类,上述方法将无法正常运行。这必须根据具体情况处理。

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

    • 对于1.9之前的版本,我首选使用site使用应用配置进行网站切换,因为documentation on initialization表示应用配置的django.contrib.admin方法是叫得比较晚。在加载应用程序模块和调用时间__init__之间,已加载模型,在某些情况下,它可能意味着模块已获取ready()的值在调用 ready()之前从site 开始。为了最大限度地降低风险,我让应用程序的django.contrib.admin代码进行了切换。

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

答案 1 :(得分: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,我真的只看到这两个选项。将所有注册调用替换为自定义管理站点,或在管理模块中明确注册模型。

答案 2 :(得分:4)

从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()。我目前正在项目中使用此解决方案。

答案 3 :(得分:3)

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

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

from django.contrib.admin.apps import AdminConfig

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

这引发了一个异常:

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'

然后异常继续:

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

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

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

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

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