Assuming I have two models:
class Profile(models.Model):
Assuming the following MySQL rating table example:
profile | category | points 1 1 10 1 1 4 1 2 10 1 3 0 1 4 10 1 4 10 1 4 10 1 5 0
I have the following values โโin my POST data, as well as other field values:
category_1_avg_val = 7 category_2_avg_val = 5 category_3_avg_val = 5 category_4_avg_val = 7 category_5_avg_val = 9
I want to filter profiles that have average ratings calculated for categories higher or equal to the required values.
Some filters are applied first as:
q1 = [('associated_with', search_for), ('profile_type__slug__exact', profile_type), ('gender__in', gender), ('rank__in', rank), ('styles__style__in', styles), ('age__gte', age_from), ('age__lte', age_to)] q1_list = [Q(x) for x in q1 if x[1]] q2 = [('user__first_name__icontains', search_term), ('user__last_name__icontains', search_term), ('profile_type__name__icontains', search_term), ('styles__style__icontains', search_term), ('rank__icontains', search_term)] q2_list = [Q(x) for x in q2 if x[1]] if q1_list: objects = Profile.objects.filter( reduce(operator.and_, q1_list)) if q2_list: if objects: objects = objects.filter( reduce(operator.or_, q2_list)) else: objects = Profile.objects.filter( reduce(operator.or_, q2_list)) if order_by_ranking_level == 'desc': objects = objects.order_by('-ranking_level').distinct() else: objects = objects.order_by('ranking_level').distinct()
Now I want to filter profiles that (on average by points) (group by category)> = (average values โโof the category included in the post)
I tried to do it one by one as
objects = objects.filter( ratings__category=1) \ .annotate(avg_points=Avg('ratings__points'))\ .filter(avg_points__gte=category_1_avg_val) objects = objects.filter( ratings__category=2) \ .annotate(avg_points=Avg('ratings__points'))\ .filter(avg_points__gte=category_2_avg_val)
But this is wrong, I think. Please help me. If return is a query that would be big.
Edited Using the answer posted by hynekcer , I came up with a slightly different solution, since I already have a set of profile requests that need to be filtered more based on the rating.
def check_ratings_avg(pr, rtd): ok = True qr = Ratings.objects.filter(profile__id=pr.id) \ .values('category')\ .annotate(points_avg=Avg('points')) qr = {i['category']:i['points_avg'] for i in qr} for cat in rtd: val = rtd[cat] if qr[cat] >= val: pass else: ok = False break return ok rtd = {1: category_1_avg_val, 2: category_2_avg_val, 3: category_3_avg_val, 4: category_4_avg_val, 5: category_5_avg_val} objects = [i for i in objects if check_ratings_avg(i, rtd)]