TL;DR
当使用通过继承扩展的ModelAdmin.get_urls
和ModelAdmins
时,是否有一种方法可以定义(名称空间的)命名良好的视图?
优选地,不求助于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
。
首选解决方案
然而,我希望能够
- 分别引用了这两个视图
- 最好在它们自己的实例名称空间中具有视图
迄今为止的进展
我想到的最好的设置是以下设置(可以直接复制粘贴到一些应用程序中进行测试):
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')
。