如何根据当前对象字段值显示不同的内联

时间:2011-11-10 02:02:26

标签: django django-admin inline-formset

给定名为MainModelRelatedModel的模型,其中后者的ForeignKey字段为MainModel

class MainModel(models.Model):
    name = models.CharField(max_length=50)
    type = models.BooleanField()

class RelatedModel1(models.Model):
    main = models.ForeingKey(MainModel):
    name = models.CharField(max_length=50)

class RelatedModel2(models.Model):
    main = models.ForeingKey(MainModel):
    name = models.CharField(max_length=50)

和相应的ModelAdmin类:

class RelatedModel1InlineAdmin(admin.TabularInline):
    model = RelatedModel1

class RelatedModel2InlineAdmin(admin.TabularInline):
    model = RelatedModel2

class MainModel(admin.ModelAdmin):
    inlines = [RelatedModel1, RelatedModel2]

这是默认行为,你得到两个内联,每个相关模型一个。问题是如何在创建MainModel实例(ModelAdmin的{​​{1}})时完全隐藏所有内联,并在显示add_view时显示内联RelatedModel1个实例的type字段为MainModel,并在True时显示RelatedModel2的内联。

我打算为False属性创建descriptor,但我意识到我需要访问正在编辑的对象实例,但它会作为参数传递。

任何帮助?

谢谢!

6 个答案:

答案 0 :(得分:4)

@Yuji' Tomita' Tomitayou的想法很好,我有同样的但是一旦尝试,我意识到你还必须删除self.inlines中的特定密钥,因为在change_viewadd_view方法self.get_inline_instances(request)之前调用get_formsets()。因此,我将内联处理移至get_form()方法。

以下是我成功的方式:

class SampleAdmin(ModelAdmin):
    inlines = []

    def get_inlines(self):
        return [SampleInline, SampleInline2]

    def get_form(self, request, obj=None, **kwargs):
        # due to django admin form fields caching you must 
        # redefine inlines on every `get_form()` call
        if (obj): self.inlines = self.get_inlines()
        for inline in self.inlines:
            # Here change condition based on your needs and manipulate
            # self.inlines as you like (remove, change, etc). 
            # I used inline.__name__ to detect if this is correct inline 
            # for my obj
            if obj.CONDITION:
                self.inlines.remove(inline)
        return super(SampleAdmin, self).get_form(request, obj, **kwargs)

答案 1 :(得分:3)

我意识到这个问题有点陈旧,而且代码库有所改变;现在有一个清晰的要点覆盖事物:c1。你可以这样做:

get_inline_instances

答案 2 :(得分:2)

您只需要在 ModelAdmin 中简单地覆盖 change_view

def change_view(self, request, object_id, form_url='', extra_context=None):
    obj = self.model.objects.filter(pk=object_id).first()
    if not obj:
        self.inlines = []
    else:
        if obj.type is True:
            self.inlines = [RelatedModel1InlineAdmin]
        else:
            self.inlines = [RelatedModel2InlineAdmin]

    return super().change_view(request,object_id,form_url=form_url,extra_context=extra_context)

这对我有用。

答案 3 :(得分:1)

contrib.admin.options.py偷看看起来你可以覆盖ModelAdmin.get_formsets。请注意,管理网站会在self.inline_instances填充__init__,因此您可能希望关注并不反复实例化您的内联。我不确定它有多贵:)

def get_formsets(self, request, obj=None):
    if not obj:
        return [] # no inlines

    elif obj.type == True:
        return [MyInline1(self.model, self.admin_site).get_formset(request, obj)]

    elif obj.type == False:
        return [MyInline2(self.model, self.admin_site).get_formset(request, obj)]

    # again, not sure how expensive MyInline(self.model, self.admin_site) is. 
    # the admin does this once. You could instantiate them and store them on the 
    # admin class somewhere to reference instead.

原始管理员get_formsets使用生成器 - 您也可以更接近地模仿原始版本:

def get_formsets(self, request, obj=None):
    for inline in self.inline_instances:
        yield inline.get_formset(request, obj)

答案 4 :(得分:1)

这对我来说在寻找旧帖子中同一问题的答案时起了作用。 根据darklow的回答,我认为您可以完全覆盖get_inline_instances,并根据您的类型添加额外的检查。

  1. 在模型中添加布尔类型检查方法

    class MainModel(models.Model):
    
        name = models.CharField(max_length=50)
    
        type = models.BooleanField()
    
        def is_type1(self):
    
           return type=="some value"
    
        def is_type2(self):
            return type=="some value"
    
  2. 在类型检查中添加内联实例 - 只需将get_inline_insances方法从父类复制并粘贴到admin.ModelAdmin类中,然后添加if块以检查模型类型,如下所示

    class MyModelAdmin(admin.ModelAdmin):
    
        inlines = [RelatedModel1, RelatedModel2]
    
        def get_inline_instances(self, request, obj=None):
            inline_instances = []
            if not obj:
                return []
            for inline_class in self.inlines:
                inline = inline_class(self.model, self.admin_site)
                if request:
                    if not (inline.has_add_permission(request) or
                                inline.has_change_permission(request, obj) or
                                inline.has_delete_permission(request, obj)):
                        continue
                    if not inline.has_add_permission(request):
                        inline.max_num = 0
                if obj.is_type1() and isinstance(inline,RelatedModel1InlineAdmin):
                    inline_instances.append(inline)
                if obj.is_type2() and isinstance(inline,RelatedModel2InlineAdmin):
                    inline_instances.append(inline)
    
            return inline_instances
    

答案 5 :(得分:0)

这是我在遇到同样问题时编写的一段代码。我想这是一种蛮力的风格,但是非常灵活,应该适合所有情况。

class MyModelAdmin(admin.ModelAdmin):
    def __init__(self, *args, **kwargs):
        super(MyModelAdmin, self).__init__(*args, **kwargs)
        self.inline_instances_hash = {}
        for inline_class in self.inlines:
            for inline_instance in self.inline_instances:
                if isinstance(inline_instance, inline_class):
                    break
            self.inline_instances_hash[inline_class] = inline_instance

    def get_inline_instance(self, inline_class):
        return self.inline_instances_hash[inline_class]

    def get_form(self, request, obj=None, **kwargs):
        if obj:
            self.inline_instances = []
            if self.CONDITION:
                self.inline_instances.append(self.get_inline_instance(
                    THE_INLINE_CLASS_I_WANT))
            #...
        else:
            self.inline_instances = self.inline_instances_hash.values()