Запрос агрегации Django для связанных объектов "один ко многим"

Вот моя упрощенная модель:

class Item(models.Model):
    pass

class TrackingPoint(models.Model):
    item = models.ForeignKey(Item)
    created = models.DateField()
    data = models.IntegerField()

    class Meta:
        unique_together = ('item', 'created')

Во многих частях моего приложения мне нужно получить набор Itemи аннотировать каждый элемент data поле из последних TrackingPoint с каждого товара, заказанного created поле. Например, экземпляр i1 класса Item имеет 3 TrackingPoint"S:

tp1 = TrackingPoint(item=i1, created=date(2010,5,15), data=23)
tp2 = TrackingPoint(item=i1, created=date(2010,5,14), data=21)
tp3 = TrackingPoint(item=i1, created=date(2010,5,12), data=120)

Мне нужен запрос для получения i1 экземпляр аннотирован tp1.data значение поля как tp1 это последняя точка отслеживания, заказанная created поле. Этот запрос также должен вернуть Itemэто что не имеют TrackingPointэто вообще. Если возможно, я предпочитаю не использовать QuerySet"s extra способ сделать это.

Это то, что я пытался до сих пор... и не удалось:(

Item.objects.annotate(max_created=Max('trackingpoint__created'),
                      data=Avg('trackingpoint__data')).filter(trackingpoint__created=F('max_created'))

Есть идеи?

2 ответа

Вот один запрос, который предоставит (TrackingPoint, Item)-пары:

TrackingPoint.objects.annotate(max=Max('item__trackingpoint__created')).filter(max=F('created')).select_related('item').order_by('created')

Вам придется запрашивать элементы без точек отслеживания отдельно.

Это не является прямым ответом на ваш вопрос, но в случае, если вам не нужно именно то, что вы описали, вас может заинтересовать решение "наибольшие числа групп". Вы можете посмотреть мой ответ на аналогичный вопрос:

Запрос Django, который получает самые последние объекты из разных категорий

- это должно относиться непосредственно к вашему делу:

 items = Item.objects.annotate(tracking_point_created=Max('trackingpoint__created')) 
 trackingpoints = TrackingPoint.objects.filter(created__in=[b.tracking_point_created for b in items])

Обратите внимание, что вторая строка может дать неоднозначные результаты, если created даты повторяются в модели TrackingPoint.

Другие вопросы по тегам