У меня есть модель Coupon, которая имеет некоторые поля, чтобы определить, если это активно, и пользовательский менеджер, который возвращает только живые купоны. Купон имеет FK к Объекту.
В запросе на Объекте я пытаюсь аннотировать количество активных доступных купонов. Однако агрегат количества, кажется, считает все купоны, не только активные.
# 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 был бы потрясающим.
Если у кого-то такая же проблема, вот как я заставил ее работать:
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
), но он работает.