Or Preiss
Or Preiss

Reputation: 59

Django filter - filter by Avg of a field in related model

I'm using django-filter to filter through my Festival model. There is another model, Review, which contains reviews about festivals and related to the Festival model by a Foreignkey. My goal is to be able to filter festivals by friendly average > 3 (friendly is a one field for example in Review model).

Any idea how to do it? Many thanks =]

Models.py

class Review(models.Model):
...
festival = models.ForeignKey(
    Festival,
    related_name='Festival_Reviews',
    on_delete=models.CASCADE,
    null=True,
    blank = True,
    default = '',
)
score_choices = (
    (1, 'Bad'),
    (2, 'Okay'),
    (3, 'Good'),
    (4, 'Great'),
    (5, 'Superb'),
)


friendly = models.IntegerField(
    choices=score_choices,
    default='',
    null=True,
    blank = True,
)

class Festival(models.Model):
...
created_at = models.DateTimeField(auto_now=True)
name = models.CharField(max_length=200)

Filter.py (this was my idea, I'm pretty new to Django)

class FestivalFilter(django_filters.FilterSet):
    ...
    Festival_Reviews_friendly = django_filters.BooleanFilter(field_name='Festival_Reviews',method='avg_above3')

    def avg_above3(self, queryset, name, value):
        return queryset.aggregate(friendly_avg=Avg('friendly')).filter(friendly_avg__gt=3)

Views.py

class HomePage(ListFilteredMixin, AjaxListView):
    template_name = 'index.html'
    page_template = 'index_page.html'
    model = models.Festival
    paginate_by = 12
    context_object_name = 'festivals'
    filter_set = FestivalFilter

Upvotes: 2

Views: 1261

Answers (1)

ruddra
ruddra

Reputation: 51988

Use it like this:

class FestivalFilter(django_filters.FilterSet):
    festival_friendly_review = django_filters.BooleanFilter(field_name='Festival_Reviews',method='avg_above3')
    class Meta:
       model = Festival
       fields = ['festival_friendly_review']

    def avg_above3(self, queryset, name, value):
            return queryset.annotate(friendly_avg=Avg('Festival_Reviews__friendly')).filter(friendly_avg__gt=3)  # use annotate instead of aggregate

Usage:

 filter = FestivalFilter({festival_friendly_review:True}, Festival.objects.all())
 print(filter.qs)
 print(filter.qs.values('friendly_avg'))

Upvotes: 2

Related Questions