Отношение Spanning для получения значения поля с помощью пунктов .annotate() и .values()

Я провожу некоторые вычисления, используя значения & annotate на queryset. Рассмотрим такую модель:

class Foo(models.Model):
   fk_bar = models.ForeignKey(to=Bar, ....)
   foo_val = models.IntegerField(...)

class Bar(models.Model):
   attr = models.Charfield(...)
   val  = models.IntegerField(...)

Так что я могу сделать:

Foo.object.all().values("fk_bar")

для группировки по внешнему отношению (некоторые Foo могут указывать на один и тот же Bar).

Тогда я могу сделать

Foo.object.all().values("fk_bar").annotate(qte=Sum("foo_val"))

Для суммы foo_val для всех объектов с одинаковым fk_bar, что дает что-то вроде:

{"fk_bar":<int>, "qte": <int>}

Однако я хочу, чтобы результирующий словарь содержал также Bar.attr, например, что-то вроде:

Foo.object.all().values("fk_bar").annotate(qte=Sum("foo_val")).annotate(bar_attr="fk_bar__attr")

Чтобы получить что-то вроде:

{"fk_bar":<int>, "qte": <int>, "bar_attr":<str>}

Однако это не удается (TypeError: Queryset.annotate() получил невыражение). Есть ли способы обойти это?

Один из вариантов - указать дополнительные значения, которые нужно "сохранить" до агрегации, в предложении values(), например, так:

Foo.object.all().values("fk_bar", "fk_bar__attr").annotate(qte=Sum("foo_val"))

Что даст примерно то, о чем спрашивают.

В общем случае, если в предложение .values() поместить больше полей, то эти поля будут доступны в выходном словаре. Любые "дополнительные" поля (например, не из необработанных данных кверисета), которые должны быть вычислены заранее (например, перед агрегацией), могут быть "созданы" с помощью .annotation() (как qte=Sum(...) выше), а затем включены в последующие .values("fk_bar", "qte", ...).

Следует отметить, что если в .values() указано много полей, то агрегация будет больше (например, 1 словарь для каждой уникальной комбинации значений). Но, по крайней мере, большая часть работы может быть сделана без сильного удара запросами по БД, а простое вычисление различных агрегаций из полученных данных путем перебора словарей должно быть достаточно быстрым, если у вас нет больших наборов данных. Например, по крайней мере, они будут работать с данными в памяти, а не многократно ударять по БД.

Иначе, похоже, следующим шагом вперед будет написание сырых sql-запросов.

Вернуться на верх