Запрос агрегации Django для связанных объектов «один ко многим» - PullRequest
3 голосов
/ 15 мая 2010

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

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 extra для этого.

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

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

Есть идеи?

Ответы [ 2 ]

8 голосов
/ 06 марта 2011

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

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

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

1 голос
/ 15 мая 2010

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

Запрос 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.

...