Может ли Django .annotate () возвращать объекты? - PullRequest
4 голосов
/ 19 апреля 2011

У меня есть набор записей, который выглядит следующим образом (посторонние данные опущены, чтобы защитить легко скучающих):

 id | user_id |            created            | units
----+---------+-------------------------------+-------
  1 |       1 | 2011-04-18 15:43:02.737063+00 |    20
  2 |       1 | 2011-04-18 15:43:02.737063+00 |     4
  3 |       1 | 2011-04-18 15:46:48.592999+00 |    -1
  4 |       1 | 2011-04-19 12:02:10.687587+00 |    -1
  5 |       1 | 2011-04-19 12:09:20.039543+00 |    -1
  6 |       1 | 2011-04-19 12:11:21.948494+00 |    -1
  7 |       1 | 2011-04-19 12:15:51.544394+00 |    -1
  8 |       1 | 2011-04-19 12:16:44.623655+00 |    -1

И я бы хотел получить результат, который выглядит следующим образом:

 id | user_id |            created            | units
----+---------+-------------------------------+-------
  8 |       1 | 2011-04-19 12:16:44.623655+00 |    14
  2 |       1 | 2011-04-18 15:43:02.737063+00 |     4

Так что я естественно смотрю на .annotate():

u = User.objects.get(pk=1)
(MyModel.objects.filter(user=u)
        .values("id","user","created")
        .annotate(stuff=Sum("units"))
)

Проблема в том, что мне нужны объекты , а не один список словарей. Мне нужно, чтобы методы, прикрепленные к этим объектам, были доступны. Есть идеи, как это сделать?

Редактировать : Я должен был указать, что я пытался использовать .values ​​(), потому что без него я бы получил кучу аннотированных объектов, но их было бы 8 (как в первом запросе выше), а не 2 (как во втором результат выше). Я предполагаю, что он не объединяет строки, потому что там есть временная метка, делающая строки разными:

MyModel.objects.filter(user=u).annotate(Sum("units"))
# Returns 8 records, not 2 as expected

Ответы [ 3 ]

5 голосов
/ 20 апреля 2011

Похоже, была некоторая путаница, когда я первоначально задал вопрос, и в результате я получил указание не использовать .values(), когда это на самом деле не было проблемой. Проблема в том, что .annotate() в Django не позволяет перезаписывать свойства столбца с вычисленными значениями, а только аннотирует объекты дополнительными данными. Это действительно имеет смысл, поскольку, если вы хотите, чтобы объект был возвращен, вы хотите иметь возможность предположить, что рассматриваемый объект действительно представляет строку в базе данных, а не путаницу вычисленных значений, перезаписывающих значения столбцов.

Тем не менее, это не сработало для меня, потому что вышеуказанная функциональность означает, что с такими столбцами, как created (временная метка), вы не можете получить желаемые рассчитанные значения без использования .values() ... не дайте мне предметы.

Итак, я выбрал следующую лучшую вещь: .raw():

query = """
    SELECT
        ep.product_id              AS product_id,
        ep.user_id                 AS user_id,
        MAX(ep.created)            AS created,
        SUM(eup.units)             AS units
    FROM
        ecom_unitpermission AS eup
    WHERE
        ep.user_id = 1
    GROUP BY
        ep.product_id,
        ep.user_id
"""

for perm in MyModel.objects.raw(query):
    print "%15s %3s %s" % (perm.product.name, perm.units, perm.product.somemethod())

Использование .defer() могло бы сделать это для меня, хотя , похоже, есть ошибка в Django 1.3 при объединении этого с .annotate()

3 голосов
/ 19 апреля 2011

Проблема не в аннотации, а в вызове значений.Документация достаточно ясно показывает, что аннотирование работает с наборами запросов.Именно вызов значений возвращает словарь значений и лишает вас возможности доступа к моделям.

Редактировать, чтобы добавить: Если вы хотите вернуть только определенные поля, но у вас есть модели, используйте defer вместо значений

0 голосов
/ 19 апреля 2011

Annotate возвращает набор запросов, но вы также вызываете .values ​​(), который всегда возвращает словарь.

...