Django - подсчет подмножества связанных моделей - необходимо аннотировать количество активных купонов для каждого предмета - PullRequest
5 голосов
/ 21 июня 2010

У меня есть модель купонов, в которой есть несколько полей, чтобы определить, активен ли он, и пользовательский менеджер, который возвращает только действующие купоны. Купон имеет FK для пункта.

В запросе на Item я пытаюсь комментировать количество доступных купонов. Тем не менее, агрегат 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 manager?


EDIT

Следующий запрос 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 ]

3 голосов
/ 21 июня 2010

Если у кого-то есть такая же проблема, вот как я заставил ее работать:

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), но он работает. 1005 *

0 голосов
/ 22 августа 2010

Вы уверены, что ваш пользовательский менеджер действительно вызван? Вы устанавливаете своего менеджера как Model.live, но запрашиваете обычного менеджера в Model.objects.

Вы пробовали следующее?

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