10

给定一个名为MainModel和 a的模型RelatedModel,其中后者有一个ForeignKey字段 to 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's add_view),并在实例字段为RelatedModel1时显示内联,并在when显示内联。typeMainModelTrueRelatedModel2False

我打算为属性创建一个描述符ModelAdmin.inline_instances,但我意识到我需要访问正在编辑的对象实例,但它是作为参数传递的。

有什么帮助吗?

谢谢!

4

6 回答 6

4

@Yuji 'Tomita' Tomitayou 这个想法很好,我也有同样的想法,但一旦尝试,我意识到你还必须从中删除特定的键,self.inlines因为 in change_viewandadd_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)
于 2013-01-29T16:04:31.217 回答
3

我意识到这个问题有点老了,代码库也发生了一些变化;现在有一个干净点可以覆盖事物:get_inline_instances. 你可以这样做:

class MainModelAdmin(models.ModelAdmin):
    inlines = [RelatedModel1InlineAdmin,RelatedModel2InlineAdmin]

    def get_inline_instances(self, request, obj=None):
        #Return no inlines when obj is being created
        if not obj:
            return []
        unfiltered = super(MainModelAdmin, self).get_inline_instances(request, obj)
        #filter out the Inlines you don't want
        if obj.type:
            return [x for x in unfiltered if isinstance(x,RelatedModel1InlineAdmin)]
        else:
            return [x for x in unfiltered if isinstance(x,RelatedModel2InlineAdmin)]
于 2017-10-17T15:49:47.533 回答
2

这对我有用,同时在这篇旧帖子中寻找相同问题的答案。扩展 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
    
于 2017-11-04T02:02:17.237 回答
2

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

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)

这对我有用。

于 2020-12-21T03:29:09.367 回答
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)
于 2011-11-10T10:27:17.627 回答
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()
于 2014-04-07T11:51:13.277 回答