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

时间:2014-01-14 22:52:11

标签: python django inheritance namespaces django-admin

TL; DR

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

最好不要诉诸ModelAdmin.model._meta或其他一些稍微有问题的解决方案。

由头

使用和继承自定义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的注册覆盖了tighten中的ScrewAdmin,但现在无法撤销ScrewAdmin.tighten

首选解决方案

但是我希望能够

  1. 分开引用两个视图和
  2. 最好在自己的实例名称空间中有视图。
  3. 到目前为止的进展

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

    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/'
    

    这很好并且有效,但包括一些hackery。

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

    (至少还有一种方法是Django的ModelAdmin.get_urls使用ModelAdmin.model._metaparametrize the view names,但之后我会使用命名空间。)

1 个答案:

答案 0 :(得分:1)

如果你看一下管理员执行它的方式here,你会发现除了定义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

然后,您需要查找您的网址:reverse('admin:app_screw_tighten')reverse('admin:app_hex_screw_tighten')

相关问题