Я спорил об этом с некоторыми коллегами. Есть ли предпочтительный способ получения объекта в Django, когда вы ожидаете только один?
Два очевидных способа:
try:
obj = MyModel.objects.get(id=1)
except MyModel.DoesNotExist:
# We have no object! Do something...
pass
И:
objs = MyModel.objects.filter(id=1)
if len(objs) == 1:
obj = objs[0]
else:
# We have no object! Do something...
pass
Первый метод кажется поведенчески более правильным, но использует исключения в потоке управления, которые могут привести к некоторым издержкам. Второе более окольное, но никогда не станет исключением.
Есть мысли о том, какой из них предпочтительнее? Что является более эффективным?
QS.get()
это хорошо. 2. Детали имеют значение: означает ли «ожидание только одного» всегда 0-1 объектов, или возможно иметь более 2 объектов, и этот случай также должен быть обработан (в этом случаеlen(objs)
это ужасная идея)? 3. Не предполагайте что-либо о накладных расходах без эталона (я думаю, что в этом случаеtry/except
будет быстрее, если хотя бы половина вызовов что-тоВы можете установить модуль с именем django-annoying и затем сделать это:
источник
1 правильно. В Python исключение имеет равные накладные расходы для возврата. Для упрощенного доказательства вы можете посмотреть на это .
2 Это то, что Django делает в бэкэнде.
get
вызываетfilter
и вызывает исключение, если элемент не найден или если найдено более одного объекта.источник
try
.Я немного опоздал на вечеринку, но в Django 1.6 есть
first()
метод для наборов запросов.https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first
Пример:
источник
Я не могу говорить с каким-либо опытом Django, но вариант # 1 ясно говорит системе, что вы запрашиваете 1 объект, тогда как второй вариант - нет. Это означает, что вариант № 1 мог бы легче использовать преимущества индексов кеша или базы данных, особенно если атрибут, по которому вы фильтруете, не гарантированно является уникальным.
Также (опять же, спекуляция) второй опции может потребоваться создать какой-либо объект для сбора результатов или объект итератора, поскольку вызов filter () обычно может возвращать много строк. Вы бы обойти это с get ().
Наконец, первый вариант и короче, и пропускает дополнительную временную переменную - только небольшая разница, но каждая помогает.
источник
Почему все это работает? Замените 4 строки одним встроенным ярлыком. (Это делает свою собственную попытку / кроме.)
источник
Model.objects.get_or_create()
дляЕще немного информации об исключениях. Если они не подняты, они почти ничего не стоят. Таким образом, если вы знаете, что, вероятно, получите результат, используйте исключение, поскольку, используя условное выражение, вы платите за проверку каждый раз, несмотря ни на что. С другой стороны, когда они вызываются, они стоят немного больше, чем условное выражение, поэтому, если вы ожидаете, что не будет результата с некоторой частотой (скажем, 30% времени, если память не изменяет), то получается условная проверка быть немного дешевле.
Но это ORM от Django, и, вероятно, обратное обращение к базе данных или даже кешированный результат, скорее всего, будут доминировать в характеристиках производительности, поэтому в этом случае предпочтение следует отдавать удобочитаемости, так как вы ожидаете только один результат - используйте
get()
.источник
Я немного поиграл с этой проблемой и обнаружил, что вариант 2 выполняет два SQL-запроса, что для такой простой задачи является чрезмерным. Смотрите мою аннотацию:
Эквивалентная версия, которая выполняет один запрос:
Переключившись на этот подход, я смог существенно сократить количество запросов, выполняемых моим приложением.
источник
Интересный вопрос, но для меня вариант № 2 пахнет преждевременной оптимизацией. Я не уверен, что является более производительным, но вариант № 1 определенно выглядит и чувствует себя более питонным для меня.
источник
Я предлагаю другой дизайн.
Если вы хотите выполнить функцию для возможного результата, вы можете получить из QuerySet, например: http://djangosnippets.org/snippets/734/
Результат довольно удивительный, вы могли бы, например:
Здесь фильтр возвращает либо пустой набор запросов, либо набор запросов с одним элементом. Ваши пользовательские функции набора запросов также могут быть цепными и многоразовыми. Если вы хотите , чтобы выполнить это для всех ваших записей:
MyModel.objects.all().yourFunction()
.Они также идеально подходят для использования в качестве действий в интерфейсе администратора:
источник
Вариант 1 более элегантный, но обязательно используйте try..except.
Исходя из собственного опыта, я могу сказать, что иногда вы уверены, что в базе данных может быть не более одного совпадающего объекта, и все же их будет два ... (за исключением, конечно, при получении объекта по его первичному ключу).
источник
Извините, что добавил еще один способ решения этой проблемы, но я использую django paginator, и в моем приложении для администрирования данных пользователю разрешено выбирать, к чему обращаться. Иногда это идентификатор документа, но в остальном это общий запрос, возвращающий более одного объекта, т. Е. Набор запросов.
Если пользователь запрашивает идентификатор, я могу запустить:
который выдает ошибку в django paginator, потому что это Record, а не Queryset of Records.
Мне нужно бежать:
Который возвращает Queryset с одним элементом в нем. Тогда пагинатор работает просто отлично.
источник
.получить()
.фильтр()
Заметка
источник