Search code examples
djangodjango-admindjango-admin-filters

Django custom SimpleListFilter


I have this two models:

class Intervencao(models.Model):
  .......
class Alvaras(models.Model):
    intervencao = models.ForeignKey(Intervencao, related_name='IntervencaoObjects2',on_delete=models.CASCADE)
    data_alv = models.DateField(blank=True,null=True,verbose_name="Data do alvaras")

I want to add a custom filter in my Intervencao.Admin, that do a query to the last record i have in Alvaras and check if the field data_alv it is empty or not. I already got it working, but i want only the last record.

class Dataalv(admin.SimpleListFilter):
    title = ('data de alvaras')
    parameter_name = 'data_alv'

    def lookups(self, request, model_admin):
        return (
            ('yes', 'yes'),
            ('no', 'no')
        )

    def queryset(self, request, queryset):
        value = self.value()
        if value == 'yes':
           return queryset.filter(IntervencaoObjects2__data_alv__isnull=False)
        elif value == "no":
            return queryset.filter(IntervencaoObjects2__data_alv__isnull=True)
            
class IntervencaoAdmin(admin.ModelAdmin):
  list_filter = Dataalv,

Solution

  • Here is a solution using Subquery:

    from django.db.models import OuterRef, Subquery
    
    class Dataalv(admin.SimpleListFilter):
        title = ('data de alvaras')
        parameter_name = 'data_alv'
    
        def lookups(self, request, model_admin):
            return (
                ('yes', 'yes'),
                ('no', 'no')
            )
    
        def queryset(self, request, queryset):
            value = self.value()
            qs = queryset.annotate(
                data_alv=Subquery(
                    (Alvaras.objects
                        .filter(intervencao_id=OuterRef('id'))
                        .order_by('-id')
                        .values('data_alv')[:1]
                    )
                )
            )
            if value == 'yes':
                return qs.filter(data_alv__isnull=False)
            if value == "no":
                return qs.filter(data_alv__isnull=True)