Как получить объект, если он существует, или None, если он не существует?

101

Когда я попрошу диспетчера модели получить объект, он поднимает DoesNotExist, когда нет соответствующего объекта.

go = Content.objects.get(name="baby")

Вместо DoesNotExist, как я могу вместо go быть None?

Теги:

15 ответов

160
Лучший ответ

Существует не "встроенный" способ сделать это. Каждый раз Django будет поднимать исключение DoNotExist. Идиоматический способ справиться с этим в python состоит в том, чтобы обернуть его в try catch:

try:
    go = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
    go = None

Что я сделал, это подкласс models.Manager, создайте safe_get, как и код выше, и используйте этот менеджер для моих моделей. Таким образом вы можете написать: SomeModel.objects.safe_get(foo='bar').

  • 10
    +1 за ловлю конкретного исключения.
  • 6
    Хорошее использование SomeModel.DoesNotExist вместо импорта исключения тоже.
Показать ещё 9 комментариев
64

Начиная с django 1.6 вы можете использовать метод first():

Content.objects.filter(name="baby").first()
  • 21
    В этом случае ошибка не возникает, если найдено более одного совпадения.
  • 2
    Мне нравится это решение, потому что при использовании objecst.get() мы получаем, что в базе данных есть либо один, либо ни одного.
Показать ещё 4 комментария
23

Из django docs

get() вызывает исключение DoesNotExist, если объект не найден для заданных параметров. Это исключение также является атрибутом класса модели. Исключение DoesNotExist наследует от django.core.exceptions.ObjectDoesNotExist

Вы можете поймать исключение и назначить None для перехода.

from django.core.exceptions import ObjectDoesNotExist
try:
    go  = Content.objects.get(name="baby")
except ObjectDoesNotExist:
    go = None
17

Вы можете создать для него общую функцию.

def get_or_none(classmodel, **kwargs):
    try:
        return classmodel.objects.get(**kwargs)
    except classmodel.DoesNotExist:
        return None

Используйте это, как показано ниже:

go = get_or_none(Content,name="baby")

go будет None, если никакая запись не соответствует else, будет возвращена запись Content.

Примечание. Это приведет к возникновению исключения. MultipleObjectsReturned, если для name= "baby" выбрано более одной записи

8

вы можете использовать exists с фильтром:

Content.objects.filter(name="baby").exists()
#returns False or True depending on if there is anything in the QS

просто альтернатива, если вы хотите узнать только, существует ли она

  • 1
    Это вызвало бы дополнительный вызов базы данных, когда существует. Не хорошая идея
  • 0
    @Christoffer не уверен, почему это будет дополнительный вызов БД. В соответствии с документами : Note: If you only want to determine if at least one result exists (and don't need the actual objects), it's more efficient to use exists().
Показать ещё 1 комментарий
8

Чтобы сделать что-то проще, вот фрагмент кода, который я написал, основываясь на материалах замечательных ответов здесь:

class MyManager(models.Manager):

    def get_or_none(self, **kwargs):
        try:
            return self.get(**kwargs)
        except ObjectDoesNotExist:
            return None

И затем в вашей модели:

class MyModel(models.Model):
    objects = MyManager()

Что это. Теперь у вас есть MyModel.objects.get(), а также MyModel.objetcs.get_or_none()

  • 5
    Также не забудьте импортировать: из django.core.exceptions import ObjectDoesNotExist
7

Вы можете сделать это следующим образом:

go  = Content.objects.filter(name="baby").first()

Теперь переменная go может быть либо желаемым объектом, либо None

Ссылка: https://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.query.QuerySet.first

7

Это одна из тех раздражающих функций, которые вы, возможно, не захотите повторно реализовать:

from annoying.functions import get_object_or_None
#...
user = get_object_or_None(Content, name="baby")
  • 0
    Я проверил код get_object_or_None но обнаружил, что он по-прежнему get_object_or_None MultipleObjectsReturned если более одного объекта. Таким образом, пользователь может подумать об окружении с помощью try-except (который уже есть в самой функции, try-except ).
6

Обработка исключений в разных точках ваших представлений действительно может быть громоздкой. Что касается определения пользовательского диспетчера моделей в файле models.py, например

class ContentManager(model.Manager):
    def get_nicely(self, **kwargs):
        try:
            return self.get(kwargs)
        except(KeyError, Content.DoesNotExist):
            return None

а затем включить его в контентный класс Model

class Content(model.Model):
    ...
    objects = ContentManager()

Таким образом, это легко можно рассматривать в представлениях i.e.

post = Content.objects.get_nicely(pk = 1)
if post:
    # Do something
else:
    # This post doesn't exist
  • 1
    Мне очень нравится это решение, но я не смог заставить его работать как есть при использовании Python 3.6. Хотел оставить примечание, что изменение возврата в ContentManager для return self.get(**kwargs) заставило его работать на меня. Не говорить, что с ответом что-то не так, просто совет для тех, кто пытается использовать его с более поздними версиями (или с тем, что мешало мне работать).
2

Если вам нужно простое однострочное решение, которое не включает обработку исключений, условные инструкции или требование Django 1.6+, сделайте это вместо этого:

x = next(iter(SomeModel.objects.filter(foo='bar')), None)
1

Я думаю, что неплохо использовать get_object_or_404()

from django.shortcuts import get_object_or_404

def my_view(request):
    my_object = get_object_or_404(MyModel, pk=1)

Этот пример эквивалентен:

from django.http import Http404

def my_view(request):
    try:
        my_object = MyModel.objects.get(pk=1)
    except MyModel.DoesNotExist:
        raise Http404("No MyModel matches the given query.")

Подробнее о get_object_or_404() в онлайн-документации django.

1

Мы можем использовать исключение Django builtin, которое привязано к моделям с именем .DoesNotExist. Таким образом, нам не нужно импортировать исключение ObjectDoesNotExist.

Вместо этого выполните:

from django.core.exceptions import ObjectDoesNotExist

try:
    content = Content.objects.get(name="baby")
except ObjectDoesNotExist:
    content = None

Мы можем это сделать:

try:
    content = Content.objects.get(name="baby")
except Content.DoesNotExist:
    content = None
1

Начиная с django 1.7, вы можете сделать так:

class MyQuerySet(models.QuerySet):

    def get_or_none(self, **kwargs):
        try:
            return self.get(**kwargs)
        except self.model.DoesNotExist:
            return None


class MyBaseModel(models.Model):

    objects = MyQuerySet.as_manager()


class MyModel(MyBaseModel):
    ...

class AnotherMyModel(MyBaseModel):
    ...

Преимущество "MyQuerySet.as_manager()" заключается в том, что будут выполняться следующие действия:

MyModel.objects.filter(...).get_or_none()
MyModel.objects.get_or_none()
1

Без исключения:

if SomeModel.objects.filter(foo='bar').exists():
    x = SomeModel.objects.get(foo='bar')
else:
    x = None

Использование исключения:

try:
   x = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
   x = None

Существует несколько аргументов о том, когда следует использовать исключение в python. С одной стороны, "легче просить прощения, чем разрешения". Хотя я согласен с этим, я считаю, что исключение должно остаться, ну, исключение, а "идеальный случай" должен работать без удара.

1

Здесь приведена вариация вспомогательной функции, которая позволяет вам в случае необходимости передать экземпляр QuerySet, если вы хотите получить уникальный объект (если он есть) из запроса, отличного от набора объектов all объектов (например, из подмножества дочерних элементов, принадлежащих родительскому экземпляру):

def get_unique_or_none(model, queryset=None, **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(**kwargs)
    except model.DoesNotExist:
        return None

Это можно использовать двумя способами, например:

  • obj = get_unique_or_none(Model, **kwargs), как обсуждалось ранее
  • obj = get_unique_or_none(Model, parent.children, **kwargs)
  • 0
    Я вижу, что django annoying уже занимается этим делом.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню