覆盖特定模型的 Django 管理 URL?

Posted

技术标签:

【中文标题】覆盖特定模型的 Django 管理 URL?【英文标题】:Override Django Admin URLs for Specific Model? 【发布时间】:2011-08-06 12:41:13 【问题描述】:

先介绍一点背景:

我有一个Event 模型,它有各种event_types。我想将其中一种事件类型“电影”分解为它自己的管理员。我有一个基本功能:继承自 Event 的代理模型,命名为 Film,该代理模型的自定义管理器将其过滤为仅“电影”事件类型,它是自己的 ModelAdmin。

问题是反过来的。我现在需要从主 Event 管理员中过滤掉电影。我不想更改Event 模型或其默认管理器,因为影响太广泛了。因此,我尝试创建另一个代理模型EventAdminProxy,其唯一目的是在管理员中提供过滤的事件列表。然后我用现有的 ModelAdmin 注册这个模型,而不是 Event

这显然有效,但它有一个不幸的副作用,即在管理员中更改 URL。变更列表不再位于“/admin/event/event/”,而是位于“/admin/event/eventadminproxy/”。

我要做的是保留此设置,但也要保留旧 URL。我尝试重载 ModelAdmin 的 get_urls 方法,但据我所知,您无法控制那里的完整 URL,只能控制“/app_label/model_class/”部分之后的内容。

我曾考虑在主 urls.py 中覆盖它,但无法找到一个可接受的视图来绑定。实际视图仅在实例化的 ModelAdmin 对象上可用,而不是类本身。

关于如何覆盖管理员中使用的 URL 的任何想法?

【问题讨论】:

【参考方案1】:

您可以覆盖 EventModelAdmin 的 queryset-method 并过滤查询集,以便排除电影事件。

类似的东西:

class EventAdmin(admin.ModelAdmin):

    def queryset(self, request):
        qs = super(EventAdmin, self).queryset(request)
        return qs.exclude(event_type='film')

【讨论】:

好的。将那个添加到“我是个白痴”类别中。不敢相信我没有想到这一点。谢谢!【参考方案2】:

查看 Django 源代码,管理 URL 构建在两个地方,在 ModelAdmin 实例和 AdminSite 实例中。

您要更改的部分内置在 AdminSite 实例 (django.contrib.admin.sites.AdminSite) 中,您可以对其进行子类化并覆盖 get_urls 方法。如果您查看该方法的后半部分,您会看到:

    # Add in each model's views.
    for model, model_admin in self._registry.iteritems():
        urlpatterns += patterns('',
            url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name),
                include(model_admin.urls))
        )

它添加了模型的 ._meta.module_name ,它只是模型名称的小写 (django.db.models.options.Options.contribute_to_class)。

一个简单的方法是覆盖站点的 get_urls 方法并为代理模型添加一个字典或特殊情况,以便它使用不同的 url 而不是 model._meta.module_name,类似的东西:

类 MyAdminSite(AdminSite):

module_name_dict = 
    EventAdminProxy: 'myfunkymodulename'


def get_urls(self):
    base_patterns = super(MyAdminSite, self).get_urls()
    my_patterns = patterns('',)

    for model, model_admin in self._registry.iteritems():
        if model in self.module_name_dict:
            module_name = self.module_name_dict[model]
            my_patterns += patterns('',
                url(r'^%s/%s/' % (model._meta.app_label, module_name),
                    include(model_admin.urls))
            )

    return my_patterns + base_patterns

【讨论】:

【参考方案3】:

您还可以继承 ChangeList 并覆盖 url_for_result() 方法来自定义更改 url,(从 another answer 学习),例如:

from django.contrib.admin.views.main import ChangeList

class FooChangeList(ChangeList):
    def url_for_result(self, obj):
        return '/foos/foo/obj.pk/'

class FooAdmin(admin.ModelAdmin):
    def get_changelist(self, request, **kwargs):
        return FooChangeList

问题的改编示例:

from django.contrib.admin.views.main import ChangeList
from django.urls import reverse

class FilmAdmin(admin.ModelAdmin):
    def get_changelist(self, request, **kwargs):
        class FilmChangeList(ChangeList):
            def url_for_result(self, obj):
                return reverse('admin:events_event_change', args=(obj.pk, ))
        return FilmChangeList

【讨论】:

以上是关于覆盖特定模型的 Django 管理 URL?的主要内容,如果未能解决你的问题,请参考以下文章

在生成 django 管理 URL 时覆盖查询集过滤器

为我的应用程序之外的单个模型覆盖 django 管理员更改表单?

如何在 django 管理面板中完全自定义 sidebar_menu?

仅针对某些模型覆盖 Django 管理员 change_list_results.html

Django:当它们已经被覆盖时不能覆盖管理模板?

Django:覆盖每个应用程序而不是每个项目的“不可覆盖”管理模板?