Search code examples
djangodjango-adminconditional-statementsinline

Conditional inline in Django admin?


I'm trying to figure out a way to display the following RelativeInline only if Person.is_member is True.

Current admin.py:

class RelativeInline(admin.TabularInline):
    model = Relative
    fk_name = 'member'

class PersonAdmin(admin.ModelAdmin):
    inlines = [RelativeInline,]
    ordering = ('first_name',)
    list_filter = ('is_member',)
    search_fields = ('first_name', 'last_name',)
    date_hierarchy = 'member_date'
    list_display = ('first_name', 'last_name', 'is_member', 'member_date', 'photo')

admin.site.register(Person, PersonAdmin)

The only hint I've been able to find is that I might be able to override get_formset, but I couldn't find a good example, so my feeble attempt didn't work.

Here's my failed attempt:

class RelativeInline(admin.TabularInline):
    model = Relative
    fk_name = 'member'

class PersonAdmin(admin.ModelAdmin):
    ordering = ('first_name',)
    list_filter = ('is_member',)
    search_fields = ('first_name', 'last_name',)
    date_hierarchy = 'member_date'
    list_display = ('first_name', 'last_name', 'is_member', 'member_date', 'photo')

    def get_formset(self, request, obj=None, **kwargs):
        if obj.is_member:
            inlines = [RelativeInline,]
        return super(PersonAdmin, self).get_formset(request, obj, **kwargs)

admin.site.register(Person, PersonAdmin)

There are no errors generated by this code, but no inline appears regardless of whether or not Person.is_member is True or False.


Update: A friend suggested I try changing:

inlines = [RelativeInline,]

to:

self.inlines = [RelativeInline,]

but to no avail. I also tried:

PersonAdmin.inlines = [RelativeInline,]

but the result was the same -- no error, no inline.


Solution

  • I decided to change the whole paradigm and solve my problem a different way. Instead of having a single admin for all Persons with a conditional inline, I decided to:

    1. Override the queryset to filter for members-only and keep RelativeInline with the admin for this model
    2. Create a proxy model and override its queryset to filter for non-members. The admin for this model does not include RelativeInline.

    In the end, I think this is a cleaner approach. Now Members can be maintained, and relatives (non-members) can be added in the inline. The NonMemberAdmin allows for editing non-members.

    models.py:

    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        last_name = models.CharField(max_length=50)
        is_member = models.BooleanField()
        is_active = models.BooleanField(default=True)
    
        class Meta:
            verbose_name_plural = 'Members'
            ordering = ('first_name', 'last_name')
    
    class PersonProxy(Person):
        class Meta:
            proxy = True
            verbose_name_plural = 'Non-Members'
    
    class Relationship(models.Model):
        name = models.CharField(max_length=50)
    
    class Relative(models.Model):
        member = models.ForeignKey(Person, related_name='relative_member')
        relative = models.ForeignKey(Person, related_name='relative_relative')
        relationship = models.ForeignKey(Relationship)
    

    admin.py:

    class RelativeInline(admin.TabularInline):
        model = Relative
        fk_name = 'member'
    
    
    class MemberAdmin(admin.ModelAdmin):
        inlines = [RelativeInline,]
        ordering = ('first_name',)
        # list_filter = ('is_member',)
        search_fields = ('first_name', 'last_name',)
        # date_hierarchy = 'member_date'
        list_display = ('first_name', 'last_name', 'member_date')
    
        def queryset(self, request):
            return (super(MemberAdmin, self).queryset(request)
                    .filter(is_member=True, is_active=True))
    
    
    class NonMemberAdmin(admin.ModelAdmin):
        ordering = ('first_name',)
        search_fields = ('first_name', 'last_name',)
        list_display = ('first_name', 'last_name')
    
        def queryset(self, request):
            return (super(NonMemberAdmin, self).queryset(request)
                    .filter(is_member=False, is_active=True))
    
    
    admin.site.register(Person, MemberAdmin)
    admin.site.register(PersonProxy, NonMemberAdmin)