Комментарии 34
Item.objects.all().only('name')
возвращает модель с Deferred Attributes. и никаких велосипедов
+3
Создание экземпляров моделей относительно медленно, а когда строк много заметно тормозит.
0
*queryset с моделями, конечно
+1
Не могли бы вы объяснить, как работают deferred attributes? Не совсем понятна официальная документация была.
Я правильно понимаю, что они вытягивают поле как values по SELECT fields, а при обращении к другим элементам, которых нет в списке делается еще запрос?
Я правильно понимаю, что они вытягивают поле как values по SELECT fields, а при обращении к другим элементам, которых нет в списке делается еще запрос?
0
Да.
DeferredAttribute — обычный дескриптор новосозданного (см db.models.deferred_class_factory) класса внутри QuerySet, который реализует нужное поведение внутри __get__.
Да, это не так быстро, как namedtuples (а изначально было создано для исключения _больших_ полей типа text из выборки, а не включения маленьких по одному) — но в случае особого желания доступа к элементам по именам делая большую выборку из большой базы, имхо, легче будет просто распаковать кортеж из values_list() внутри тела цикла.
In [26]: o = pa.model.objects.all().only('name')[0]
In [27]: o
Out[27]: <Product_Deferred_list_of_deferred_fields: someproduct>
In [28]: dict(o.__class__.__dict__)
Out[28]:
{...
'ops': <db.models.query_utils.DeferredAttribute object at 0x7eb6150c>,
...}
In [30]: o.ops
Out[30]: u'лалала'
In [31]: connection.queries[-2:]
Out[31]:
[{'sql': u'SELECT "product"."id", "product"."name" FROM "product" LIMIT 1',
'time': '0.000'},
{'sql': u'SELECT "product"."id", "product"."ops" FROM "product" WHERE "ipc_product"."id" = 1 ',
'time': '0.000'}]
DeferredAttribute — обычный дескриптор новосозданного (см db.models.deferred_class_factory) класса внутри QuerySet, который реализует нужное поведение внутри __get__.
Да, это не так быстро, как namedtuples (а изначально было создано для исключения _больших_ полей типа text из выборки, а не включения маленьких по одному) — но в случае особого желания доступа к элементам по именам делая большую выборку из большой базы, имхо, легче будет просто распаковать кортеж из values_list() внутри тела цикла.
+3
Point = namedtuple('Point', 'x y'.split())
Простите, что за индусский код? Почему не
Point = namedtuple('Point', ('x', 'y'))
+7
Причём тут индусский код? Нормальная идиома, особенно, для знакомых с перлом.
Это консольная сессия в конце концов, меньше знаков препинания — хорошо.
Это консольная сессия в конце концов, меньше знаков препинания — хорошо.
-6
Более того, в документации английским по белому указан вариант
namedtuple(«name», «a b»)
namedtuple(«name», «a b»)
+1
немного не так )
collections.namedtuple(typename, field_names, verbose=False, rename=False)
…
The field_names are a single string with each fieldname separated by whitespace and/or commas, for example 'x y' or 'x, y'. Alternatively, field_names can be a sequence of strings such as ['x', 'y'].
collections.namedtuple(typename, field_names, verbose=False, rename=False)
…
The field_names are a single string with each fieldname separated by whitespace and/or commas, for example 'x y' or 'x, y'. Alternatively, field_names can be a sequence of strings such as ['x', 'y'].
-2
namedtuple позволяет передавать в качестве имён полей строку с именами, разделёнными пробелами и запятыми — и вот это действительно говнокод, да ещё противоречащий духу питона, но тем не менее… в стандартную библиотеку попал.
-2
Оптимизировать нужно знать что, есть ли какие-то замеры насколько «меньше, быстрее» и т.д.? Может такая оптимизация со сложными новыми туплами и не дает много.
-1
Даже если она не даёт ничего, она сделает мой код красивее. Да и даёт, особенно если в кеш складывать, туплы и пиклить быстрее и памяти меньше занимают.
-1
По-моему начались дни джанги на Хабре.
+7
Мне кажется, это определенно должно быть встроено в фреймворк, поэтому создал feature request в багтрекере джанги с этой штукой.
Спасибо, буду использовать вместо values_list и values. Оверхед тут совсем маленький.
Спасибо, буду использовать вместо values_list и values. Оверхед тут совсем маленький.
0
Вообще-то, я написал письмецо в django-developers, пока решили оставить это в дикой природе. Посмотреть как пойдёт.
0
Сомневаюсь что прокатит.
Это добавит 2.6 в ограничения.
Это добавит 2.6 в ограничения.
0
Именованные кортежи для python 2.4: code.activestate.com/recipes/500261/
0
Я, как правило, делаю так:
По-моему, это вполне красиво и читабельно. И никакие велосипеды не нужны.
В случае с
может возникнуть путаница, потому что на первый взгляд создаётся впечатление, что
И ещё исправьте, пожалуйста. Вместо
qs = Item.objects.filter(...).values_list('title', 'amount', 'price')
for title, amount, price in qs:
print title, amount
total += amount * price
По-моему, это вполне красиво и читабельно. И никакие велосипеды не нужны.
В случае с
qs = Item.objects.filter(...).namedtuples('title', 'amount', 'price')
for item in qs:
print item.title, item.amount
total += item.amount * item.price
может возникнуть путаница, потому что на первый взгляд создаётся впечатление, что
item
— это полноценная модель.И ещё исправьте, пожалуйста. Вместо
Items.objects....
, должно быть Item.objects....
+4
Хм… Поупражнялся с вашим кодом, и правда удобно и оверхеда практически нет.
In [8]: %timeit Product.objects.all().values_list('title', 'unit_price')
10000 loops, best of 3: 160 us per loop
In [9]: %timeit Product.objects.all().namedtuples('title', 'unit_price')
10000 loops, best of 3: 162 us per loop
In [10]: Product.objects.count()
Out[10]: 1867
0
Хм… Я тоже для этого
.only('name1', 'name2')
использовал. По моему, описываемую вами проблему покрывает полностью но при этом более гибкое и прозрачное.-1
Спасибо, отличное решение!
А как бы сделть так, чтобы получать не список, а словарь, где ключом являлось бы значение заданного поля?
Типа:
Я часто использую такой подход для оптимизации выборок (вместо join-ов):
1. Получаем список объектов
2. Пробегаемся по списку и набираем необходимые id-шники связанных объектов
3. Получаем связанные объекты используя SELECT… IN (id, ...)
4. Прицепляем связанные объекты к исходному списку (либо во view, либо непосредственно в темплейтах)
Соответственно в 3-ем пункте мне нужны записи сгруппированные по определённому полю. Я до сих пор не выработал удобный подход для подобных манипуляций. Может быть вы что-то подскажете?
А как бы сделть так, чтобы получать не список, а словарь, где ключом являлось бы значение заданного поля?
Типа:
votes = Vote.objects.filter(object_id = 1, content_type=1).namedtuples('value', 'vote_time', key='user_id')
user_vote = votes.get(user.pk)
Я часто использую такой подход для оптимизации выборок (вместо join-ов):
1. Получаем список объектов
2. Пробегаемся по списку и набираем необходимые id-шники связанных объектов
3. Получаем связанные объекты используя SELECT… IN (id, ...)
4. Прицепляем связанные объекты к исходному списку (либо во view, либо непосредственно в темплейтах)
Соответственно в 3-ем пункте мне нужны записи сгруппированные по определённому полю. Я до сих пор не выработал удобный подход для подобных манипуляций. Может быть вы что-то подскажете?
0
У джанговского QuerySet-а есть стандартный метод in_bulk(). Он делает именно то, что вам нужно. Правда, будет ли он работать с namedtuples неизвестно.
Но всегда можно использовать:
Но всегда можно использовать:
objs_by_pk = dict((obj.pk, obj) for obj in queryset)
0
in_bulk я использую когда связка происходит по первичному ключу, но это не всегда так. Пример из жизни: фильм можно оценить и/или написать не него отзыв. На странице фильма нужно вывести список лучших отзывов вместе с оценками тех, кто их написал. Лучшие отзывы определяются голосованием, а значит нужно выбрать ещё и голоса текущего пользователя за отзывы. Как я это делаю:
С именнованными кортежами я поэкспериментировал. Модифицировать ваш код, так чтобы он мог группировать по ключу у меня не вышло, но, самым неприятным оказался тот факт, что в namedtuple нельзя добавить новые поля…
- Получаю несколько лучших отзывов
- Выбираю из полученного списка первичные ключи и id пользователей
- Получаю голоса выбранных пользователей и добавляю их в список отзывов. Тут связка отзыв.user_id→оценка.user_id
- Получаю и связываю голоса текущего пользователя за отзывы. Связка отзыв.id→оценка.object_id
- Ну и, заодно, выбираю самих авторов отзывов, т.к. INNER JOIN генерируемый select_related() почти всегда даёт filesort в MySQL. Связка отзыв.user_id→пользователь.id. И только в этом случае мне подходит in_bulk()
С именнованными кортежами я поэкспериментировал. Модифицировать ваш код, так чтобы он мог группировать по ключу у меня не вышло, но, самым неприятным оказался тот факт, что в namedtuple нельзя добавить новые поля…
0
> Модифицировать ваш код, так чтобы он мог группировать по ключу у меня не вышло
Это было бы неправильно с точки зрения архитектуры. Если часто нужно группировать, добавьте свой метод на queryset, который будет выдавать словарь по нужному ключу. Или не метод, шорткат.
> самым неприятным оказался тот факт, что в namedtuple нельзя добавить новые поля
Это, всё-таки, tuple. И зачем?
Это было бы неправильно с точки зрения архитектуры. Если часто нужно группировать, добавьте свой метод на queryset, который будет выдавать словарь по нужному ключу. Или не метод, шорткат.
> самым неприятным оказался тот факт, что в namedtuple нельзя добавить новые поля
Это, всё-таки, tuple. И зачем?
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Именованные кортежи из выборок