Making inlines conditional in the Django admin

后端 未结 8 2325
长发绾君心
长发绾君心 2021-02-07 21:45

I have a model that I want staff to be able to edit up to the date for the event. Like this:

class ThingAdmin(admin.ModelAdmin):
    model = Thing

    if obj.da         


        
相关标签:
8条回答
  • 2021-02-07 22:23

    In recent version of Django, you'll need to override 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)
    
    0 讨论(0)
  • 2021-02-07 22:30

    As of Django 2.2.2 (current latest version as of this writing), I would use the solution provided earlier by @aggieNick02, which is to override get_inline_instances shown below.

    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)
    

    I'm posting this new answer because as of April 17th, 2019 in this commit, it looks like the future recommended way to do this would be to instead override the get_inlines method. So in later versions, the solution to this could look like the code below, which allows you to specify different sets of inlines and use them based on a condition.

    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
    
    0 讨论(0)
  • 2021-02-07 22:32

    The most turnkey way to do this now is to override and super call to 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]
    

    This puts MyInline in when you want it and not when you don't. If you know the only inline you have in your class is MyInline, you can make it even simpler:

    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 讨论(0)
  • 2021-02-07 22:34

    I think the best answer is in the django documentation: https://docs.djangoproject.com/en/1.9/ref/contrib/admin/

    Search for "get_inline_instances" The example provided is very good and the nuances of the call are described in detail.

    0 讨论(0)
  • 2021-02-07 22:36

    Thanks to the comments for a change in 1.4. My implementation here wasn't thread safe either, so it really should have been deleted.

    Since get_formsets is passed the object and calls get_inline_instances, we can modify both functions to act on the object.

    This should work:

    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)
    
    0 讨论(0)
  • 2021-02-07 22:39

    I had a complex case where the solutions I tried failed in unexpected ways (problems with readonly fields in inlines). This is the most clear and failsafe way I've found:

    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)
    

    This is working in Django 1.4.x.

    0 讨论(0)
提交回复
热议问题