使用模型(admin)继承时,Django命名ModelAdmin.get_urls的方法是什么



TL;DR

当使用通过继承扩展的ModelAdmin.get_urlsModelAdmins时,是否有一种方法可以定义(名称空间的)命名良好的视图?

优选地,不求助于ModelAdmin.model._meta或性质稍有问题的某种其它解决方案。

Pretext

通过get_urls添加的视图名称在使用和继承自定义ModelAdmins时会被覆盖。

也就是说,视图名称admin:tighten在以下示例中被覆盖:

class Screw(models.Model):
"A screw"
class HexCapScrew(Screw):
"A hex cap screw"

class ScrewAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super(ScrewAdmin, self).get_urls()
extra_urls = patterns('',
url(r'^tighten/$', self.tighten, name='tighten'),
)
return extra_urls + urls
def tighten(self, request):
pass
class HexCapScrewAdmin(ScrewAdmin):
pass

admin.site.register(Screw, ScrewAdmin)
admin.site.register(HexCapScrew, HexCapScrewAdmin)

在shell上发生以下情况:

In [1]: reverse('admin:tighten')
Out[1]: u'/admin/parts/hexscrew/tighten/'

这当然是可以理解的,因为HexCapScrewAdmin的注册覆盖了ScrewAdmin中的tighten,但是现在不可能反转ScrewAdmin.tighten

首选解决方案

然而,我希望能够

  1. 分别引用了这两个视图
  2. 最好在它们自己的实例名称空间中具有视图

迄今为止的进展

我想到的最好的设置是以下设置(可以直接复制粘贴到一些应用程序中进行测试):

from django.contrib import admin
from django.db import models
class Screw(models.Model):
"A screw"
class Meta:
app_label = 'parts'

class HexCapScrew(Screw):
"A hex cap screw"
class Meta:
app_label = 'parts'
proxy = True

class ScrewAdmin(admin.ModelAdmin):
def tighten(self, request):
pass
def get_urls(self):
urls = super(ScrewAdmin, self).get_urls()
extra_urls = patterns('',
url(r'^tighten/$', self.tighten, name='tighten'),
)
# Find out the slugified name of the model this admin is bound to
# TODO: Feels dirty
model_name = self.model._meta.model_name
# Add the to `extra_urls` to their own namespace
namespaced_extra_urls = patterns('',
url(r'^', include(extra_urls, namespace=model_name, app_name='screw')),
)
return namespaced_extra_urls + urls

class HexCapScrewAdmin(ScrewAdmin):
pass
admin.site.register(Screw, ScrewAdmin)
admin.site.register(HexCapScrew, HexCapScrewAdmin)

现在我有以下内容:

In [1]: reverse('admin:screw:tighten')
Out[1]: u'/admin/parts/screw/tighten/'
In [2]: reverse('admin:hexscrew:tighten')
Out[2]: u'/admin/parts/hexscrew/tighten/'
In [3]: reverse('admin:screw:tighten', current_app='hexscrew')
Out[3]: u'/admin/parts/hexscrew/tighten/'

这很好,很有效,但也包含了一些技巧。

这是最好的,还是我只是错过了什么?有什么建议吗?

(至少还有一种方法是,Django的ModelAdmin.get_urls使用ModelAdmin.model._meta来参数化视图名称,但随后我将使用名称空间。)

如果你看看管理员在这里做这件事的方式,你会发现除了定义url之外,模型管理员还将app_label和model_name作为url名称的前缀,从而从一开始就避免了子类化问题。它还具有保护视图不受未经授权用户(使用self.admin_site.admin_view装饰器)攻击的优点。您的get_urls()方法将变为:

def get_urls(self):
from django.conf.urls import url
def wrap(view):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs)
return update_wrapper(wrapper, view)
info = self.model._meta.app_label, self.model._meta.model_name
urlpatterns = super(ScrewAdmin, self).get_urls()
urlpatterns.append(
url(r'^tighten/$', wrap(self.tighten), name='%s_%s_tighten' % info))
return urlpatterns

然后,你可以查找你的url,比如:reverse('admin:app_screw_tighten')reverse('admin:app_hex_screw_tighten')

最新更新