一个Category模型,为相关模型管理员创建代理模型

时间:2013-11-27 20:22:25

标签: django django-admin

所以我在尝试创建一个模型时遇到了一些麻烦,该模型将定义管理管理站点中相关模型的动态代理模型。我知道这句话令人困惑,所以我只会分享我的代码。

models.py

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

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

我想要实现的是,在管理站点中,不是为Tag模型设置一个ModelAdmin,对于每个类别,我将为所有相关标签设置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)

非常感谢任何指导或帮助

2 个答案:

答案 0 :(得分:1)

动态ModelAdmins与admin注册模型的方式不兼容。 我建议在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方法,则需要确保在variouse管理模板中呈现的链接指向正确的网址(例如,使用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 restart aproach ...是的,你可以重启Django。这取决于您部署实例的方式。 在apache上,您可以触摸将重新加载实例os.utime(path/to/wsgi.py的wsgi文件。 使用uwsgi时,您可以使用uwsgi.reload()

您可以在保存翻译(views.py)后检查Rosetta源代码如何重新启动实例。

答案 1 :(得分:0)

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

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)。有没有人知道一个合适的解决方案?