Когда я попрошу диспетчера модели получить объект, он поднимает DoesNotExist
, когда нет соответствующего объекта.
go = Content.objects.get(name="baby")
Вместо DoesNotExist
, как я могу вместо go
быть None
?
Когда я попрошу диспетчера модели получить объект, он поднимает DoesNotExist
, когда нет соответствующего объекта.
go = Content.objects.get(name="baby")
Вместо DoesNotExist
, как я могу вместо go
быть None
?
Существует не "встроенный" способ сделать это. Каждый раз 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')
.
Начиная с django 1.6, вы можете использовать метод first() следующим образом:
Content.objects.filter(name="baby").first()
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
Вы можете создать для него общую функцию.
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" выбрано более одной записи
Чтобы сделать что-то проще, вот фрагмент кода, который я написал, основываясь на материалах замечательных ответов здесь:
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()
Вы можете сделать это следующим образом:
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
вы можете использовать exists
с фильтром:
Content.objects.filter(name="baby").exists()
#returns False or True depending on if there is anything in the QS
просто альтернатива, если вы хотите узнать только, существует ли она
Обработка исключений в разных точках ваших представлений действительно может быть громоздкой. Что касается определения пользовательского диспетчера моделей в файле 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
Это одна из тех раздражающих функций, которые вы, возможно, не захотите повторно реализовать:
from annoying.functions import get_object_or_None
#...
user = get_object_or_None(Content, name="baby")
Если вам нужно простое однострочное решение, которое не включает обработку исключений, условные инструкции или требование Django 1.6+, сделайте это вместо этого:
x = next(iter(SomeModel.objects.filter(foo='bar')), None)
Я думаю, что неплохо использовать 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.
Здесь приведена вариация вспомогательной функции, которая позволяет вам в случае необходимости передать экземпляр 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)
Без исключения:
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. С одной стороны, "легче просить прощения, чем разрешения". Хотя я согласен с этим, я считаю, что исключение должно остаться, ну, исключение, а "идеальный случай" должен работать без удара.
Начиная с 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()
Мы можем использовать исключение 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
Хорошая идея - использовать метод get_object_or_404 для надлежащей обработки этого случая https://docs.djangoproject.com/en/2.2/intro/tutorial03/#a-shortcut-get-object-or-404