Отношение 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-запросов.