Django - 计算相关模型的子集 - 需要为每个项目注释活动优惠券的数量

时间:2010-06-21 02:40:23

标签: sql django orm count aggregates

我有一个优惠券模型,其中包含一些字段来定义它是否处于活动状态,以及一个仅返回实时优惠券的自定义管理器。优惠券有FK到物品。

在对项目的查询中,我正在尝试注释可用的有效优惠券数量。但是,Count聚合似乎在计算所有优惠券,而不仅仅是活动优惠券。

# models.py
class LiveCouponManager(models.Manager):
    """
    Returns only coupons which are active, and the current
    date is after the active_date (if specified) but before the valid_until
    date (if specified).
    """
    def get_query_set(self):
        today = datetime.date.today()
        passed_active_date = models.Q(active_date__lte=today) | models.Q(active_date=None)
        not_expired = models.Q(valid_until__gte=today) | models.Q(valid_until=None)
        return super(LiveCouponManager,self).get_query_set().filter(is_active=True).filter(passed_active_date, not_expired)

class Item(models.Model):
    # irrelevant fields

class Coupon(models.Model):
    item = models.ForeignKey(Item)
    is_active = models.BooleanField(default=True)
    active_date = models.DateField(blank=True, null=True)
    valid_until = models.DateField(blank=True, null=True)
    # more fields

    live = LiveCouponManager() # defined first, should be default manager

# views.py
# this is the part that isn't working right
data = Item.objects.filter(q).distinct().annotate(num_coupons=Count('coupon', distinct=True))

由于其他原因,.distinct()distinct=True位是存在的 - 查询是这样的,它将返回重复项。这一切都很好,只是在这里提到完整性。

问题是Count包含由自定义管理员过滤掉的非活动优惠券。

我是否可以指定Count应该使用live经理?


修改

以下SQL查询正是我所需要的:

SELECT data_item.title, COUNT(data_coupon.id) FROM data_item LEFT OUTER JOIN data_coupon ON (data_item.id=data_coupon.item_id)
WHERE (
    (is_active='1') AND 
    (active_date <= current_timestamp OR active_date IS NULL) AND
    (valid_until >= current_timestamp OR valid_until IS NULL)
)
GROUP BY data_item.title

至少在sqlite上。任何SQL大师的反馈都会非常感激 - 我觉得我在这里偶然编程。或者,更好的是,翻译回Django ORM语法会很棒。

2 个答案:

答案 0 :(得分:3)

如果其他人遇到同样的问题,我就是这样做的:

Items = Item.objects.filter(q).distinct().extra(

            select={"num_coupons":
                     """
                     SELECT  COUNT(data_coupon.id) FROM  data_coupon
                     WHERE (
                         (data_coupon.is_active='1') AND 
                         (data_coupon.active_date <= current_timestamp OR data_coupon.active_date IS NULL) AND
                         (data_coupon.valid_until >= current_timestamp OR data_coupon.valid_until IS NULL) AND
                         (data_coupon.data_id = data_item.id)
                     )
                     """
                    },).order_by(order_by)

我不知道我认为这是一个'正确'的答案 - 它以一种可能不可移植的方式完全复制我的自定义管理器(我不确定它是多么可移植current_timestamp),但它确实有效

答案 1 :(得分:0)

你确定你的自定义经理真的被叫了吗?您将经理设置为Model.live,但是您在Model.objects查询正常经理。

您是否尝试过以下操作?

data = Data.live.filter(q)...