Есть ли какая-либо функция Django, которая позволит мне получить объект из базы данных или None, если ничего не соответствует?
Сейчас я использую что-то вроде:
foo = Foo.objects.filter(bar=baz)
foo = len(foo) > 0 and foo.get() or None
Но это не очень понятно, и это беспорядочно повсюду.
В Django 1.6 вы можете использовать метод first()
Queryset. Он возвращает первый объект, соответствующий запросу, или None, если нет соответствующего объекта.
Использование:
p = Article.objects.order_by('title', 'pub_date').first()
first()
работает точно так, как он полагает. Возвращает первый объект в результате запроса и не заботится, если найдено несколько результатов. Если вам нужно проверить наличие нескольких возвращенных объектов, используйте вместо этого .get()
.
Есть два способа сделать это:
try:
foo = Foo.objects.get(bar=baz)
except model.DoesNotExist:
foo = None
Или вы можете использовать обертку:
def get_or_none(model, *args, **kwargs):
try:
return model.objects.get(*args, **kwargs)
except model.DoesNotExist:
return None
Назовите его так:
foo = get_or_none(Foo, baz=bar)
Чтобы добавить некоторый пример кода в sorki answer (я бы добавил это как комментарий, но это мой первый пост, и у меня недостаточно репутации, чтобы оставлять комментарии), я внедрил пользовательский менеджер get_or_none следующим образом:
from django.db import models
class GetOrNoneManager(models.Manager):
"""Adds get_or_none method to objects
"""
def get_or_none(self, **kwargs):
try:
return self.get(**kwargs)
except self.model.DoesNotExist:
return None
class Person(models.Model):
name = models.CharField(max_length=255)
objects = GetOrNoneManager()
И теперь я могу это сделать:
bob_or_none = Person.objects.get_or_none(name='Bob')
Вы также можете попробовать django раздражать (у него есть еще одна полезная функция!)
установите его с помощью:
pip install django-annoying
from annoying.functions import get_object_or_None
get_object_or_None(Foo, bar=baz)
Дайте Foo свой пользовательский менеджер. Это довольно просто - просто введите свой код в пользовательский менеджер, установите пользовательский менеджер в своей модели и вызовите его с помощью Foo.objects.your_new_func(...)
.
Если вам нужна универсальная функция (использовать ее на любой модели, а не только с помощью настраиваемого менеджера), напишите свой собственный и поместите ее где-нибудь на свой путь python и импортируйте, а не беспорядочно.
Если вы выполняете это через менеджер или общую функцию, вы также можете поймать "MultipleObjectsReturned" в инструкции TRY, так как функция get() будет повышать ее, если ваши kwargs извлекают более одного объекта.
Основываясь на общей функции:
def get_unique_or_none(model, *args, **kwargs):
try:
return model.objects.get(*args, **kwargs)
except (model.DoesNotExist, model.MultipleObjectsReturned), err:
return None
и в менеджере:
class GetUniqueOrNoneManager(models.Manager):
"""Adds get_unique_or_none method to objects
"""
def get_unique_or_none(self, *args, **kwargs):
try:
return self.get(*args, **kwargs)
except (self.model.DoesNotExist, self.model.MultipleObjectsReturned), err:
return None
Здесь приведена вариация вспомогательной функции, которая позволяет вам в случае необходимости передать экземпляр QuerySet
, если вы хотите получить уникальный объект (если он есть) из запроса, отличного от набора объектов all
объектов (например, из подмножества дочерних элементов, принадлежащих родительскому экземпляру):
def get_unique_or_none(model, queryset=None, *args, **kwargs):
"""
Performs the query on the specified `queryset`
(defaulting to the `all` queryset of the `model` default manager)
and returns the unique object matching the given
keyword arguments. Returns `None` if no match is found.
Throws a `model.MultipleObjectsReturned` exception
if more than one match is found.
"""
if queryset is None:
queryset = model.objects.all()
try:
return queryset.get(*args, **kwargs)
except model.DoesNotExist:
return None
Это можно использовать двумя способами, например:
obj = get_unique_or_none(Model, *args, **kwargs)
, как обсуждалось ранееobj = get_unique_or_none(Model, parent.children, *args, **kwargs)
django annoying
уже занимается этим делом.
Я думаю, что в большинстве случаев вы можете просто использовать:
foo, created = Foo.objects.get_or_create(bar=baz)
Только если не важно, что новая запись будет добавлена в таблицу Foo (другие столбцы будут иметь значения None/default)
len(foo)
- это плохо : « Примечание. Не используйте len () в QuerySets, если все, что вам нужно, это определить количество записей в наборе. Намного эффективнее обрабатывать счет на уровне базы данных, используя SQL SELECT COUNT (), и Django предоставляет метод count () именно по этой причине. " Переписано:foo = foo[0] if foo.exists() else None