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

发布于 2024-12-14 22:25:01 字数 1259 浏览 4 评论 0原文

给定一个名为 MainModel 的模型和一个 RelatedModel,其中后者具有 MainModelForeignKey 字段:

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 实例(ModelAdminadd_view)时完全隐藏所有内联,并显示当 MainModel 实例的 type 字段为 True 时,内联 RelatedModel1,并显示 的内联相关模型2错误

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

有什么帮助吗?

谢谢!

Given a model named MainModel and a RelatedModel, where the later has a ForeignKey field 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)

and the corresponding ModelAdmin classes:

class RelatedModel1InlineAdmin(admin.TabularInline):
    model = RelatedModel1

class RelatedModel2InlineAdmin(admin.TabularInline):
    model = RelatedModel2

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

And that's the default behavior, you get two inlines, one for every related model. The question is how to hide completely all the inlines when the MainModel instance is being created (the ModelAdmin's add_view), and to show the inlines for RelatedModel1 when the type field of the MainModel instance is True, and show the inlines for RelatedModel2 when False.

I was going to create a descriptor for the ModelAdmin.inline_instances attribute, but I realized that I need access to the object instance being edited, but it is passed around as parameters.

Any help?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

☆獨立☆ 2024-12-21 22:25:01

@Yuji 'Tomita' Tomitayou 这个想法很好,我也有同样的想法,但一旦尝试,我意识到你还必须从 self.inlines 中删除特定的键,因为在 change_view 和 < code>add_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)

@Yuji 'Tomita' Tomitayou the idea was good, i had the same but once trying, i realized you must also remove specific key from self.inlines because in change_view and add_view method self.get_inline_instances(request) is called before get_formsets(). Therefore i moved inlines handling to get_form() method.

Here is how i sucessfully did it:

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)
送君千里 2024-12-21 22:25:01

我意识到这个问题有点老了,代码库也发生了一些变化;现在有一个干净的点可以覆盖一些东西: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)]

I realize this question's a bit old and the codebase has changed a bit; there's a cleanish point to override things at now: get_inline_instances. You can do this:

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)]
吐个泡泡 2024-12-21 22:25:01

您只需在 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)

这对我来说很有效。

You need just simply override change_view in 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)

that's work for me.

小霸王臭丫头 2024-12-21 22:25:01

在这篇旧文章中寻找同一问题的答案时,这对我有用。
扩展 darklow 的答案,我认为您可以简单地完全覆盖 get_inline_instances 并根据您的类型添加额外的检查。

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

    类 MainModel(models.Model):
    
        名称 = models.CharField(max_length=50)
    
        类型 = models.BooleanField()
    
        def is_type1(自身):
    
           返回类型==“某个值”
    
        def is_type2(自身):
            返回类型==“某个值”
    
  2. 基于类型检查添加内联实例 - 只需将父类中的 get_inline_insances 方法复制并粘贴到 admin.ModelAdmin 类中,然后添加 if 块检查模型类型,如下所示

    类 MyModelAdmin(admin.ModelAdmin):
    
        内联= [相关模型1,相关模型2]
    
        def get_inline_instances(self, request, obj=None):
            内联实例 = []
            如果不是对象:
                返回 []
            对于 self.inlines 中的 inline_class:
                内联 = inline_class(self.model, self.admin_site)
                如果要求:
                    如果不是(inline.has_add_permission(请求)或
                                inline.has_change_permission(request, obj) 或
                                inline.has_delete_permission(请求, obj)):
                        继续
                    如果不是 inline.has_add_permission(request):
                        内联.max_num = 0
                如果 obj.is_type1() 和 isinstance(inline,RelatedModel1InlineAdmin):
                    inline_instances.append(内联)
                如果 obj.is_type2() 和 isinstance(inline,RelatedModel2InlineAdmin):
                    inline_instances.append(内联)
    
            返回内联实例
    

This worked for me while searching for an answer to the same problem in this old post.
Expanding upon darklow's answer , I think you can simply override get_inline_instances completely and add an extra check based on your type.

  1. Add a boolean type check method in your model

    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. Add inline instance base on type check - Simply copy and paste the get_inline_insances method from the parent class into your admin.ModelAdmin class and add the if block to check the model type as shown below

    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
    
爱人如己 2024-12-21 22:25:01

从查看 contrib.admin.options.py 看来您可以覆盖 ModelAdmin.get_formsets。请注意,管理站点在 __init__ 处填充 self.inline_instances,因此您可能希望遵循而不是一遍又一遍地实例化您的内联。我不确定它有多贵:)

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.

原始的 admin get_formsets 使用生成器 - 你也可以更接近地模仿原始的:

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

From peeking at contrib.admin.options.pyLooks like you could override ModelAdmin.get_formsets. Note that the admin site populates self.inline_instances at __init__, so you probably want to follow and not instantiate your inlines over and over. I'm not sure how expensive it is : )

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.

The original admin get_formsets uses generators - you could too to more closely mimic the original:

def get_formsets(self, request, obj=None):
    for inline in self.inline_instances:
        yield inline.get_formset(request, obj)
陌生 2024-12-21 22:25:01

这是我遇到同样问题时编写的一段代码。我想,这有点暴力风格,但非常敏捷,应该适合所有情况。

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()

Here is a piece of code I wrote when I was faced with the same problem. It is a bit brute force style, I guess, but is very agile and should suit all cases.

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()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文