【发布时间】:2012-10-25 15:53:36
【问题描述】:
假设我有两个模型:
class Profile(models.Model):
#some fields here
class Ratings(models.Model):
profile = models.ForeignKey(profile)
category = models.IntegerField()
points = models.IntegerField()
假设以下 MySQL 表“评级”示例:
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
我的 POST 数据和其他字段值中有以下值:
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
我想过滤平均评分高于或等于所需值的类别的配置文件。
一些过滤器最初应用为:
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()
现在我想过滤其(平均点数)(按类别分组)>=(帖子中类别的平均值)的配置文件
我试着一一做到这一点
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)
但我认为这是错误的。请帮帮我。如果 return 是一个很好的查询集。
已编辑
使用hynekcer 发布的答案,我想出了稍微不同的解决方案,因为我已经查询了需要根据评级进行更多过滤的配置文件集。
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)]
【问题讨论】:
-
我认为类的名称(模型 Profile 和 Ratings)应该像在 Django 中一样大写。否则字段名称
profile很容易被同名的模型混淆。 -
是的,你说得对,实际上类名在我的 models.py 文件中是大写的。只是忘了在这里做。关于 acuall 问题的任何想法?
-
如果第一个“黑盒”过滤器会选择数百万个配置文件,您建议的通过编辑添加的第二个解决方案可能会非常慢。运行数百万个 SQL 查询是一场噩梦。尽可能多的应该由一个查询集完成。请问,您对第一个“黑匣子”过滤器有什么看法?它是仅基于一张表(配置文件)还是更多?能不能只写更多
filter和exclude类型的条件,最终也有Q和F对象条件,但是没有任何聚合函数?请在您的问题中描述这些附加限制的类型。 -
@hynekcer 我已经更新了我的问题,请检查
some filters are applied initially as:问题部分中的黑盒过滤器。如您所见,如果用户只选择free text search,q1_list将是空的,并且会有很多配置文件。如果q2_list将被使用,那么配置文件将更少。但仍然不确定会有多少个人资料。所以需要最优解。 -
Conditional Annotations我想你了:(