为相关模型管理员创建代理模型的类别模型

Posted

技术标签:

【中文标题】为相关模型管理员创建代理模型的类别模型【英文标题】:A Category model which creates proxy models for related model admin 【发布时间】:2013-12-13 16:20:38 【问题描述】:

所以我在尝试创建一个模型来定义动态代理模型来管理管理站点中的相关模型时遇到了一些麻烦。我知道那句话很混乱,所以我只分享我的代码。

models.py

class Cateogry(models.Model):
    name = models.CharField(...)

class Tag(models.Model):
    name = models.CharField(...)
    category = models.ForeignKey(Cateogry)

我想要实现的是,在管理站点中,不是为标签模型设置一个 ModelAdmin,而是为每个类别设置一个用于所有相关标签的模型管理员。我使用this answer 实现了这一点。假设我有一个名为 A 的类别:

def create_modeladmin(modeladmin, model, name = None):
    class  Meta:
        proxy = True
        app_label = model._meta.app_label

    attrs = '__module__': '', 'Meta': Meta

    newmodel = type(name, (model,), attrs)

    admin.site.register(newmodel, modeladmin)
    return modeladmin

class CatA(TagAdmin):
    def queryset(self, request):
        qs = super(CatA, self).queryset(request)
        return qs.filter(cateogry = Cateogry.objects.filter(name='A'))

create_modeladmin(CatA, name='CategoryAtags', model=Tag)

但这还不够好,因为显然我仍然需要手动子类化 TagAdmin 模型,然后运行 ​​create_modeladmin。我需要做的是遍历所有 Category 对象,为每个对象创建一个 Tagadmin 的动态子类(以类别命名),然后从中创建一个动态代理模型,这就是我开始旋转的地方。

for cat in Category.objects.all():
    NewSubClass = #somehow create subclass of TagAdmin, the name should be '<cat.name>Admin' instead of NewSubClass 
    create_modeladmin(NewSubClass, name=cat.name, model=Tag)

任何指导或帮助将不胜感激

【问题讨论】:

【参考方案1】:

动态模型管理员与管理员注册模型的方式不能很好地协同工作。 我建议在 CategoryAdmin 中创建子视图。

from django.conf.urls import patterns, url
from django.contrib import admin
from django.contrib.admin.options import csrf_protect_m
from django.contrib.admin.util import unquote
from django.core.urlresolvers import reverse

from demo_project.demo.models import Category, Tag

class TagAdmin(admin.ModelAdmin):
    # as long as the CategoryTagAdmin class has no custom change_list template
    # there needs to be a default admin for Tags
    pass
admin.site.register(Tag, TagAdmin)

class CategoryTagAdmin(admin.ModelAdmin):
    """ A ModelAdmin invoked by a CategoryAdmin"""

    read_only_fields = ('category',)

    def __init__(self, model, admin_site, category_admin, category_id):
        self.model = model
        self.admin_site = admin_site
        self.category_admin = category_admin
        self.category_id = category_id
        super(CategoryTagAdmin, self).__init__(model, admin_site)

    def queryset(self, request):
        return super(CategoryTagAdmin, self).queryset(request).filter(category=self.category_id)


class CategoryAdmin(admin.ModelAdmin):

    list_display = ('name', 'tag_changelist_link')

    def tag_changelist_link(self, obj):
        info = self.model._meta.app_label, self.model._meta.module_name
        return '<a href="%s" >Tags</a>' % reverse('admin:%s_%s_taglist' % info, args=(obj.id,))
    tag_changelist_link.allow_tags = True
    tag_changelist_link.short_description = 'Tags'

    @csrf_protect_m
    def tag_changelist(self, request, *args, **kwargs):
        obj_id = unquote(args[0])
        info = self.model._meta.app_label, self.model._meta.module_name

        category = self.get_object(request, obj_id)

        tag_admin = CategoryTagAdmin(Tag, self.admin_site, self, category_id=obj_id )

        extra_context = 
            'parent': 
                'has_change_permission': self.has_change_permission(request, obj_id),
                'opts': self.model._meta,
                'object': category,
            ,
        
        return tag_admin.changelist_view(request, extra_context)


    def get_urls(self):
        info = self.model._meta.app_label, self.model._meta.module_name
        urls= patterns('', 
            url(r'^(.+)/tags/$', self.admin_site.admin_view(self.tag_changelist), name='%s_%s_taglist' % info )
        )
        return urls + super(CategoryAdmin, self).get_urls()


admin.site.register(Category, CategoryAdmin)

类别更改列表中的项目有一个额外的列,其中包含一个由tag_changelist_link 指向CategoryAdmin.tag_changelist 的链接。此方法创建一个具有一些附加功能的 CategoryTagAdmin 实例并返回其 changelist_view。

这样您就可以在每个类别上都有一个过滤的标签更改列表。要修复 tag_changelist 视图的面包屑,您需要将 CategoryTagAdmin.change_list_template 设置为 % extends 'admin/change_list.html' % 并覆盖 % block breadcrumbs % 的自己的模板。这就是您需要来自 extra_context 的 parent 变量来创建正确 URL 的地方。

如果您计划实现 tag_changeviewtag_addview 方法,您需要确保在各种管理模板中呈现的链接指向正确的 url(例如,使用 form_url 作为参数调用 change_view)。 CategoryTagAdmin 上的 save_model 方法可以在添加新标签时设置默认类别。

def save_model(self, request, obj, form, change):
    obj.category_id = self.category_id
    super(CategoryTagAdmin, self).__init__(request, obj, form, change)

如果你仍然想坚持 apache 重启方法......是的,你可以重启 Django。这取决于您如何部署实例。 在 apache 上,您可以触摸将重新加载实例 os.utime(path/to/wsgi.py 的 wsgi 文件。 使用 uwsgi 时可以使用uwsgi.reload()

您可以查看Rosetta 的源代码,他们在保存翻译后如何重启实例(views.py)。

【讨论】:

这是一些该死的奇妙代码。尽管它还不完美,但它确实向我展示了正确的方向,并且您还向我指出了如何重新启动服务器的选项。非常感谢,你得到了赏金,你应得的!【参考方案2】:

所以我找到了一个半解决方案。

def create_subclass(baseclass, name):
    class Meta:
        app_label = 'fun'

    attrs = '__module__': '', 'Meta': Meta, 'cat': name 
    newsub = type(name, (baseclass,), attrs)
    return newsub

class TagAdmin(admin.ModelAdmin):
    list_display = ('name', 'category')
    def get_queryset(self, request):
        return Tag.objects.filter(category = Category.objects.filter(name=self.cat))

for cat in Category.objects.all():
    newsub = create_subclass(TagAdmin, str(cat.name))
    create_modeladmin(newsub, model=Tag, name=str(cat.name))

它正在工作。但是每次添加新类别时,都需要在服务器显示之前刷新服务器(因为 admin.py 是在运行时评估的)。有没有人知道一个体面的解决方案?

【讨论】:

以上是关于为相关模型管理员创建代理模型的类别模型的主要内容,如果未能解决你的问题,请参考以下文章

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

Django admin:代理模型的用户权限

模型没有出现在 django admin 中

如何在 django 的自引用模型中优化相关记录的检索?

如何使用 Django 模型在 ExtJS 网格中获取外键值而不是键?

svm是一种典型的啥模型