Django: Получить объект из БД или "Нет", если ничего не соответствует

Есть ли какая-либо функция Django, которая позволит мне получить объект из базы данных или None, если ничего не соответствует?

Сейчас я использую что-то вроде:

foo = Foo.objects.filter(bar=baz)
foo = len(foo) > 0 and foo.get() or None

Но это не очень понятно, и это беспорядочно повсюду.

Ответ 1

В Django 1.6 вы можете использовать метод first() Queryset. Он возвращает первый объект, соответствующий запросу, или None, если нет соответствующего объекта.

Использование:

p = Article.objects.order_by('title', 'pub_date').first()

Ответ 2

Есть два способа сделать это:

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)

Ответ 3

Чтобы добавить некоторый пример кода в 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')

Ответ 4

Вы также можете попробовать django раздражать (у него есть еще одна полезная функция!)

установите его с помощью:

pip install django-annoying

from annoying.functions import get_object_or_None
get_object_or_None(Foo, bar=baz)

Ответ 5

Дайте Foo свой пользовательский менеджер. Это довольно просто - просто введите свой код в пользовательский менеджер, установите пользовательский менеджер в своей модели и вызовите его с помощью Foo.objects.your_new_func(...).

Если вам нужна универсальная функция (использовать ее на любой модели, а не только с помощью настраиваемого менеджера), напишите свой собственный и поместите ее где-нибудь на свой путь python и импортируйте, а не беспорядочно.

Ответ 6

Если вы выполняете это через менеджер или общую функцию, вы также можете поймать "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

Ответ 7

Здесь приведена вариация вспомогательной функции, которая позволяет вам в случае необходимости передать экземпляр 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)

Ответ 8

Я думаю, что в большинстве случаев вы можете просто использовать:

foo, created = Foo.objects.get_or_create(bar=baz)

Только если не важно, что новая запись будет добавлена ​​в таблицу Foo (другие столбцы будут иметь значения None/default)