【问题标题】:Django Count Aggregation with Filter on Related Field Not WorkingDjango 计数聚合与相关字段上的过滤器不起作用
【发布时间】:2018-04-01 17:32:15
【问题描述】:

假设我有 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 OR max_uses > n_user_redemptions 的优惠券来做到这一点。这是我在 Coupon 类上尝试的方法:

@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))

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

在任何地方都没有提到用户?

【问题讨论】:

    标签: python django django-models django-rest-framework


    【解决方案1】:

    所以我从来没有得到答案或弄清楚为什么聚合过滤器不起作用,但我确实找到了一个不太漂亮的解决方法,使用子查询至少不是原始 sql,它会带来维护噩梦,而不是迫使我评估多个查询。我更喜欢使用聚合,因为它使用引擎盖下的连接而不是子查询,这将是更有效的方法,但现在这将服务,我将与 Django 团队一起登录以查看聚合过滤器的情况。

    解决方法如下:

    coupon_redemption_count = Count(Subquery(UserRedemption.objects.filter(user=user, coupon=OuterRef(‘pk’)).values_list(‘id’, flat=True)))
    

    那么其他一切都是一样的。

    留下这个问题以防万一有人发现它并告诉我为什么聚合过滤器不起作用。

    【讨论】:

      【解决方案2】:

      你就不能这样吗:

      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
      

      它更简单,也有点粗糙,但它可以很好地完成工作。

      【讨论】:

      • 当然这样可以完成,但效率非常低。我需要生产质量。我在这里的第一个选择是只编写原始 sql,因为这个 sql 实际上并不是非常复杂。我在问为什么计数聚合过滤器不起作用,因为我不想编写和管理原始 sql,而且当我从 Django 几乎逐行撕掉这段代码时,我不明白为什么它不起作用文档
      猜你喜欢
      • 2020-01-30
      • 2015-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-07
      • 1970-01-01
      • 2021-12-13
      • 2017-03-09
      相关资源
      最近更新 更多