#python #django #annotate
#python #django #аннотировать
Вопрос:
У меня есть набор записей, который выглядит следующим образом (посторонние данные опущены, чтобы защитить легко вводимые в заблуждение):
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
Комментарии:
1. Что касается второй части проблемы, аннотации, я не совсем понимаю, как первый набор данных приводит ко второму… Почему идентификаторы 8 и 2 остаются с суммой 14 и 4? Аннотирование не уменьшает количество возвращаемых строк. Возможно, если бы вы добавили SQL для того, чего вы пытались достичь
2. Какой обычный sql-запрос вы использовали для создания результирующей таблицы (2 строки), отображаемой выше
Ответ №1:
Похоже, что при первоначальном задании вопроса возникла некоторая путаница, и в результате мне было предписано не использовать .values()
, когда на самом деле это не было проблемой. Проблема в том, что функция Django .annotate()
не допускает перезаписи свойств столбца вычисляемыми значениями и только аннотирует объекты дополнительными данными. Это действительно имеет смысл, поскольку, если вы хотите вернуть объект, вы хотите иметь возможность предположить, что рассматриваемый объект действительно представляет строку в базе данных, а не мешанину вычисленных значений, перезаписывающих значения столбцов.
Однако у меня это не сработало, потому что вышеупомянутая функциональность означает, что с такими столбцами, как 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 "s %3s %s" % (perm.product.name, perm.units, perm.product.somemethod())
Использование .defer()
могло бы сделать это за меня, хотя похоже, что в Django 1.3 есть ошибка при объединении этого с .annotate()
Ответ №2:
Проблема не в аннотации, а в вызове значений. Документация довольно четко показывает, что annotate работает с наборами запросов. Именно вызов values возвращает словарь значений и лишает вас возможности доступа к моделям.
Отредактируйте, чтобы добавить: Если вы хотите вернуть только определенные поля, но у вас все еще есть модели, тогда используйте defer вместо значений
Комментарии:
1. На самом деле, я добавил .values(), потому что без нее я не смог бы объединить результат в две строки, которые я ожидал. Я думаю, это связано с тем фактом, что у меня там есть временная метка, поэтому все строки разные.
Ответ №3:
Annotate возвращает набор запросов, но вы также вызываете .values(), который всегда будет возвращать словарь.