django-url-filter icon indicating copy to clipboard operation
django-url-filter copied to clipboard

Support filtering on aggregate fields.

Open ahmohamed opened this issue 8 years ago • 2 comments

I am not sure how complex to implement this, but it would be really useful if we can filter on aggregate (annotated) fields.

Exmaple: Consider these models

from django.db import models

class Reporter(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    email = models.EmailField()

class Article(models.Model):
    headline = models.CharField(max_length=100)
    pub_date = models.DateField()
    reporter = models.ForeignKey(Reporter)

And the DRF viewset

from rest_framework import viewsets

class ReporterViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = models.Reporter.objects.annotate(article_count=Count("reporter_set"))

    filter_backends = [DjangoFilterBackend]
    filter_fields = ['last_name', 'article_count']

We filter reporters that have at least n articles by reporter?article_count__gte=5.

This setup currently result in error Reporter has no field named 'article_count'. Since the filtering is done on the queryset not the model, I am not sure why the model is being checked.

Thanks.

ahmohamed avatar May 11 '17 10:05 ahmohamed

Thanks for the suggestion. Reason why it checks agains a model is because you are using a ModelFilterSet which gets the fields from the model definition in order to get the form class for the filter to use for data validation. Will try to implement in future however not sure how that will work since annotated fields might be loosing their data-type so I would not know which form class to use for those fields.

Meanwhile you should be able to manually define a filterset for that field. Something like:

class MyFilterSet(ModelFilterSet):
    article_count = Filter(form_class=forms.IntegerField())
    class Meta(object):
        model = Reporter

you can always see what filters are set on the filterset by doing a representation of the filterset:

print(repr(MyFilterSet()))

miki725 avatar May 11 '17 14:05 miki725

Thank you for the quick response! The work around is working very fine for now.

ahmohamed avatar May 11 '17 23:05 ahmohamed