Search code examples
pythondjangodjango-adminweb-development-serverdjango-admin-filters

django admin - subquery inside list_filter


Given are the following models

class Report(models.Model):
    id = models.CharField(max_length=256, unique=True)
    name = models.CharField(max_length=256)
    confidential = models.BooleanField(default=False)

class Owner(models.Model):
    first_name = models.CharField(max_length=512)
    last_name = models.CharField(max_length=512)
    title = models.CharField(max_length=512)
    birth_date = models.CharField(max_length=512)
    company = models.CharField(max_length=512)
    address = models.CharField(max_length=512)
    city = models.CharField(max_length=512)
    last_login = models.DateTimeField(auto_now=True)
    access_granted = models.DateTimeField(auto_now_add=True)
    report = models.ForeignKey(Report, on_delete=models.CASCADE)

and the following admin.py from django

class OwnerAdmin(admin.ModelAdmin):
    .
    .
    .
    list_filter = ('report__name',)

As expected I get the option to filter based on the name of the report. Yet I would like to only get displayed to filter if the report is confidential, means that the confidential field of the given report is true. How can i achieve this?


Solution

  • If I am understanding your question correctly, you would like to only list the confidential reports in your list_filter. To do this you will need to create a custom filter:

    class ReportListFilter(admin.SimpleListFilter):
    
        title = 'Confidential Reports'
        parameter_name = 'confidential_reports'
    
        def lookups(self, request, model_admin):
            confidential_reports = Report.objects.filter(confidential=True)
            return tuple(
                (report.id, report.name) for report in confidential_reports
            )
    
        def queryset(self, request, queryset):
            if self.value()
                return queryset.filter(report_id=self.value())
            return queryset.all()
    
    

    To understand the above, you can read more about it in the docs, but a summary is that:

    • title: gives the title for your filter
    • parameter_name: gives the string that will be used in the URL when your filter is used
    • lookups returns a list of tuples that gives the values for the filter
    • queryset is what does the actual filtering.

    Then you just need to add it into your OwnerAdmin and everything should work:

    class OwnerAdmin(admin.ModelAdmin):
        .
        .
        .
        list_filter = (ReportListFilter,)