在Django管理员中使条件成为有条件的

时间:2011-03-23 21:00:06

标签: django django-admin conditional inlines

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

class ThingAdmin(admin.ModelAdmin):
    model = Thing

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

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

请指教?

9 个答案:

答案 0 :(得分:10)

感谢对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)

答案 1 :(得分:3)

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

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。

答案 2 :(得分:2)

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

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)

答案 3 :(得分:2)

您可以使用 (django 3.0+) get_inlines 方法。您所要做的就是覆盖该方法并定义您的逻辑,

class ThingInline(admin.StackedInline):
    """ inline needs to be returned """
    models = ThingModel


class ThingAdmin(admin.ModelAdmin):
    model = Thing
    inlines = []

    def get_inlines(self, request, obj):
        if obj.date < today:    # the date
            return [ThingInline]
        # or else
        return []

更新: 通过这种方法,我遇到了 this issue,因此可以通过覆盖 change_view() 方法来完成相同的事情,而不是使用上述方法,

class ThingAdmin(admin.ModelAdmin):
    model = Thing
    inlines = []

    def change_view(self, request, object_id, form_url='', extra_context=None):
        self.inlines = []
    
        try:
            obj = self.model.objects.get(pk=object_id)
        except self.model.DoesNotExist:
            pass
        else:
            if obj.date < today:
                self.inlines = [ThingInline,]

        return super(ThingAdmin, self).change_view(request, object_id, form_url, extra_context)

答案 4 :(得分:0)

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

我能够使用以下代码获得为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)

在故事/ admin.py

class StoryAdmin(HighlightAdmin):

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

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

我认为最简单的方法是在get_fieldsget_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)

答案 7 :(得分:0)

现在最常用的方法是覆盖和超级调用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)

答案 8 :(得分:0)

从Django 2.2.2(撰写本文时为当前最新版本)开始,我将使用@ aggieNick02先前提供的解决方案,该解决方案将覆盖下面显示的get_inline_instances

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)

我之所以发布此新答案,是因为截至2019年4月17日,在this commit中,看来未来建议的执行此方法的方法是改写get_inlines方法。因此,在更高版本中,解决方案可能类似于下面的代码,该代码允许您指定不同的内联集并根据条件使用它们。

class ThingAdmin(admin.ModelAdmin):
    model = Thing

    inlines = [inline]
    other_set_of_inlines = [other_inline]

    def get_inlines(self, request, obj):
        if obj.date > datetime.date(2012, 1, 1):
            return self.inlines
        else:
            return self.other_set_of_inlines