问题 在Django管理员中设置内联条件


我有一个模型,我希望工作人员能够编辑到事件的日期。喜欢这个:

class ThingAdmin(admin.ModelAdmin):
    model = Thing

    if obj.date < today: #Something like that
        inlines = [MyInline,]

问题是,我无权访问此级别的obj实例。我已经尝试重写get_formset(),但没有得到任何结果。

请指教?


1294
2018-03-23 21:00


起源



答案:


感谢对1.4中的更改的评论。我在这里的实现也不是线程安全的,所以它确实应该被删除。

以来 get_formsets 传递对象并调用 get_inline_instances,我们可以修改两个函数来对象进行操作。

这应该工作:

class ThingAdmin(admin.ModelAdmin):
    model = Thing

    inlines = [inline]
    other_set_of_inlines = [other_inline]

    def get_inline_instances(self, request, obj=None):
        #                                    ^^^ this is new
        inline_instances = []

        if obj.date > datetime.date(2012, 1, 1):
            inlines = self.inlines
        else:
            inlines = self.other_set_of_inlines

        for inline_class in inlines:
            inline = inline_class(self.model, self.admin_site)
            if request:
                if not (inline.has_add_permission(request) or
                        inline.has_change_permission(request) or
                        inline.has_delete_permission(request)):
                    continue
                if not inline.has_add_permission(request):
                    inline.max_num = 0
            inline_instances.append(inline)
        return inline_instances

    def get_formsets(self, request, obj=None):
        for inline in self.get_inline_instances(request, obj):
            #                                           ^^^^^ this is new
            yield inline.get_formset(request, obj)

10
2018-03-23 22:08



工作完美无瑕。谢谢。 - Jason Goldstein
这不适用于Django 1.4。 __init__ 似乎不再创建self.inline_instances,但现在有一个get_inline_instances()方法可以显式覆盖。 - Cerin
似乎Cerin的评论现已过时,因为此答案已被修改为使用get_inline_instances - Andy Baker
这对我有用,直到我想根据数据是否填写来显示/隐藏内联。我想说 if obj.fieldX and obj.fieldX == "some text": [show inline "I"]。但是这给了我管理表单错误,我假设因为在POST结束之前将内联添加到表单中而不是像我预期的那样在GET中添加。我的解决方法是获取保存的对象,并在该对象上显示/隐藏内联而不是“obj”参数。即 if obj: saved_obj = ModelName.objects.get(pk = obj.pk) 然后检查saved_obj的字段而不是obj的字段。 (django 1.8) - jenniwren
另外,在django 1.8中我不需要覆盖“get_formsets”。 - jenniwren


答案:


感谢对1.4中的更改的评论。我在这里的实现也不是线程安全的,所以它确实应该被删除。

以来 get_formsets 传递对象并调用 get_inline_instances,我们可以修改两个函数来对象进行操作。

这应该工作:

class ThingAdmin(admin.ModelAdmin):
    model = Thing

    inlines = [inline]
    other_set_of_inlines = [other_inline]

    def get_inline_instances(self, request, obj=None):
        #                                    ^^^ this is new
        inline_instances = []

        if obj.date > datetime.date(2012, 1, 1):
            inlines = self.inlines
        else:
            inlines = self.other_set_of_inlines

        for inline_class in inlines:
            inline = inline_class(self.model, self.admin_site)
            if request:
                if not (inline.has_add_permission(request) or
                        inline.has_change_permission(request) or
                        inline.has_delete_permission(request)):
                    continue
                if not inline.has_add_permission(request):
                    inline.max_num = 0
            inline_instances.append(inline)
        return inline_instances

    def get_formsets(self, request, obj=None):
        for inline in self.get_inline_instances(request, obj):
            #                                           ^^^^^ this is new
            yield inline.get_formset(request, obj)

10
2018-03-23 22:08



工作完美无瑕。谢谢。 - Jason Goldstein
这不适用于Django 1.4。 __init__ 似乎不再创建self.inline_instances,但现在有一个get_inline_instances()方法可以显式覆盖。 - Cerin
似乎Cerin的评论现已过时,因为此答案已被修改为使用get_inline_instances - Andy Baker
这对我有用,直到我想根据数据是否填写来显示/隐藏内联。我想说 if obj.fieldX and obj.fieldX == "some text": [show inline "I"]。但是这给了我管理表单错误,我假设因为在POST结束之前将内联添加到表单中而不是像我预期的那样在GET中添加。我的解决方法是获取保存的对象,并在该对象上显示/隐藏内联而不是“obj”参数。即 if obj: saved_obj = ModelName.objects.get(pk = obj.pk) 然后检查saved_obj的字段而不是obj的字段。 (django 1.8) - jenniwren
另外,在django 1.8中我不需要覆盖“get_formsets”。 - jenniwren


我有一个复杂的案例,我尝试的解决方案以意想不到的方式失败(内联中只读字段的问题)。这是我发现的最明确和最安全的方式:

class MyAdmin(admin.ModelAdmin):

    def add_view(self, request, form_url='', extra_context=None):
        self.inlines = [InlineA, InlineC]
        return super(MyAdmin, self).add_view(request, form_url, extra_context)

    def change_view(self, request, object_id, form_url='', extra_context=None):
        self.inlines = [InlineB, InlineC, InlineD]
        return super(MyAdmin, self).change_view(request, object_id, form_url, extra_context)

这适用于Django 1.4.x.


3
2017-07-25 21:08





在最新版本的Django中,您需要覆盖ModelAdmin.get_formsets。例如

class MyAdmin(admin.ModelAdmin):

    def get_formsets(self, request, obj=None):
        if obj:
            for _ in super(MyAdmin, self).get_formsets(request, obj):
                yield _
        else:
            for inline in self.get_specific_inlines(request):
                yield inline.get_formset(request, obj)

2
2018-02-12 22:14





我遇到的情况是,我需要根据您为特定故事提供的管理网站显示内联。

我能够使用以下代码获得为Django 1.3工作的动态内联:

在highlight / admin.py中

class HighlightInline(generic.GenericTabularInline):
    model = Highlight
    extra = 1
    max_num = 4
    fields = ('order', 'highlight')
    template = 'admin/highlights/inline.html'

class HighlightAdmin(admin.ModelAdmin):
    def regulate_highlight_inlines(self):
        highlights_enabled = Setting.objects.get_or_default('highlights_enabled', default='')
        highlight_inline_instance = HighlightInline(self.model, self.admin_site)
        highlight_found = any(isinstance(x, HighlightInline) for x in self.inline_instances)
        if highlights_enabled.strip().lower() == 'true':
            if not highlight_found:
                self.inline_instances.insert(0, highlight_inline_instance)
        else:
            if highlight_found:
                self.inline_instances.pop(0)
        print self.inline_instances

    def change_view(self, request, object_id, form_url='', extra_context=None):
        self.regulate_highlight_inlines()
        return super(HighlightAdmin, self).change_view(request, object_id)

    def add_view(self, request, form_url='', extra_context=None):
        self.regulate_highlight_inlines()   
        return super(HighlightAdmin, self).add_view(request, form_url, extra_context)

在story / admin.py中

class StoryAdmin(HighlightAdmin):

需要注意的一点是,我不仅仅是在操作内联类(HighlightInline),而是在改变内联实例(HighlightInline(self.model,self.admin_site))。这是因为django已经在admin类的初始构造期间基于内联类列表构建了内联实例列表。


0
2018-06-15 14:30





我认为最好的答案是在django文档中: https://docs.djangoproject.com/en/1.9/ref/contrib/admin/

搜索“get_inline_instances”提供的示例非常好,并详细描述了调用的细微差别。


0
2018-06-15 06:26





我认为最简单的方法就是调用你的自定义功能 get_fields, 要么 get_fieldsets 等等,只是设定 self.inlines 在自定义功能中。

class XXXAdmin(admin.ModelAdmin):
    def set_inlines(self, request, obj):
        """ hack inlines models according current request.user or obj """
        self.inlines = []
        if request.user.is_superuser or request.user is obj.recorder:
            self.inlines = [AbcInline, ]

    def get_fields(self, request, obj=None):
        self.set_inlines(request, obj)  # NOTICE this line
        super(XXXAdmin, self).get_fields(request, obj)

0
2018-01-16 07:04





现在最常见的方法是覆盖和超级调用get_inline_instances。

class ThingAdmin(models.ModelAdmin):
    inlines = [MyInline,]

    def get_inline_instances(self, request, obj=None):
        unfiltered = super(ThingAdmin, self).get_inline_instances(request, obj)
        #filter out the Inlines you don't want
        keep_myinline = obj and obj.date < today
        return [x for x in unfiltered if not isinstance(x,MyInline) or keep_myinline]

这可以在您需要时将MyInline放入,而不是在您不需要时。如果您知道您的班级中唯一的内联是MyInline,那么您可以使其更简单:

class ThingAdmin(models.ModelAdmin):
    inlines = [MyInline,]

    def get_inline_instances(self, request, obj=None):
        if not obj or obj.date >= today:
            return []
        return super(ThingAdmin, self).get_inline_instances(request, obj)

0
2017-10-17 16:00