假设我有2个模型Coupon和UserRedemption(当然还有用户模型)。他们看起来像:
Coupon(models.Model):
offer = models.TextField() # not important to this task
limited_use = models.BooleanField(default=False)
max_uses = models.IntegerField(default=0)
UserRedemption(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
coupon = models.ForeignKey(Coupon)
非常基本。我的应用程序中有优惠券,每张优惠券都有各种用户兑换的优惠券清单。有些优惠券是限制使用",即它们只能由给定用户使用一定次数。
我的目标:返回优惠券列表,不包括用户提出请求时使用过最多次数的优惠券。
我尝试通过使用当前用户兑换的次数(n_user_redemptions)来注释优惠券并过滤优惠券的列表,其中limited_use = False或max_uses> n_user_redemptions。这是我在优惠券类上尝试的方法:
@classmethod
def get_available_user_coupons(cls, user):
coupon_redemption_count = Count('userredemption', filter=Q(userredemption__user=user))
return cls.objects.annotate(n_user_redemptions=coupon_redemption_count)
.filter(Q(limited_use=False) | Q(max_uses__gt=F('n_user_redemptions')))
但是,对于从请求传入的用户,它没有正确过滤。它总是将n_user_redemptions设置为所有用户的所有兑换优惠券的计数,而不仅仅是给定用户的兑换。我已经测试过,如果limited_use设置为False,并且该优惠券已被兑换的总次数(所有用户数)超过max_uses,则会正确返回优惠券。我已经确认我正确地传递了用户。
我不确定我在这里失踪了什么。我已经非常广泛地浏览了django聚合文档,看起来这应该可行。任何帮助将不胜感激。
进一步更新:
我检查了生成的SQL,并注意到无论我是否设置了查询都是相同的:
coupon_redemption_count = Count('userredemption', filter=Q(userredemption__user=user))
OR
coupon_redemption_count = Count('userredemption')
不确定过滤器参数的行为应该在Count中。
相关的SQL:
SELECT "coupon_coupon"."id", "coupon_coupon"."offer", "coupon_coupon"."limited_use", "coupon_coupon"."max_uses",
COUNT("userredemption_userredemption"."id") AS "n_user_redemptions"
FROM "coupon_coupon"
LEFT OUTER JOIN "userredemption_userredemption"
ON ("coupon_coupon"."id" = "userredemption_userredemption"."coupon_id")
GROUP BY "coupon_coupon"."id"
ORDER BY "coupon_coupon"."id" DESC
在任何地方都没有提到用户?
答案 0 :(得分:1)
你不能这样做:
def return_list_of_coupons(user):
list_of_coupons = [coupon for coupon in Coupon.objects.all()
if UserRedemption.objects.filter(user=user.pk, coupon=coupon.pk).count() < coupon.max_uses]
return list_of_coupons
它更简单,有点粗糙,但它的工作做得很好。
答案 1 :(得分:0)
所以我从来没有得到答案或弄清楚为什么聚合过滤器不起作用,但我确实找到了一个不太漂亮的解决方法,使用一个子查询,至少不是原始的sql,这将提出维护噩梦而不是强迫我评估多个查询。我更喜欢使用聚合,因为它使用引擎下的连接而不是子查询,这将是更有效的方法,但现在将服务,我将与Django团队一起登录以查看聚合过滤器的内容。
以下是解决方法:
coupon_redemption_count = Count(Subquery(UserRedemption.objects.filter(user=user, coupon=OuterRef(‘pk’)).values_list(‘id’, flat=True)))
然后其他一切都是一样的。
保留此问题以防万一找到它并告诉我为什么聚合过滤器无效。