如何获得该对象(如果存在)或“无”(如果不存在)?


222

当我要求模型管理器获取一个对象时,DoesNotExist当没有匹配的对象时,它将引发。

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

相反的DoesNotExist,我怎么能有go可以None代替?

Answers:


332

没有“内置”方式可以做到这一点。Django每次都会引发DidNotExist异常。在python中处理此问题的惯用方式是将其包装在try catch中:

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

我所做的就是将模型.Manager子类化,创建safe_get类似于上面的代码,并将该经理用于我的模型。这样,您可以编写:SomeModel.objects.safe_get(foo='bar')


7
很好地使用SomeModel.DoesNotExist而不是导入异常。
supermitch

194
该解决方案长四行。对我来说,这太多了。在django 1.6中,您可以使用SomeModel.objects.filter(foo='bar').first()此方法返回第一个匹配项,或者返回None。如果有多个实例,它不会失败queryset.get()
guettli 2014年

9
我认为过度使用异常处理默认情况是不好的方式。是的,“请求宽恕比请求许可容易”。但是在我看来,应该仍然使用例外作为例外。
康斯坦丁·舒伯特

8
显式胜于隐式。除非出于性能原因使用,否则filter().first()我认为应该采取例外措施。
christianbundy

6
如果您不关心是否有多个整数,那么仅使用first()只是一个好主意。否则,此解决方案会更好,因为如果您意外找到多个对象,它仍然会引发Exception,这通常是您在这种情况下想要发生的。
rossdavidh

181

从Django 1.6开始,您可以像这样使用first()方法:

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

26
在这种情况下,如果有多个匹配项,则不会引发错误。
康斯坦丁·舒伯特

7
'FeroxTL',您需要在这个答案上加上@guettli的信誉,因为他在发布您的帖子之前一年就已接受的答案发表了评论。
colm.anseo,2016年

7
@colminator我想说guettli如果想提高自己的stackoverflow信誉,应该知道一个新答案不属于注释:) FeroxTL应该获得使隐藏在注释中的内容更清晰作为答案的要点。我认为您的评论对guettli足够信誉,如果那是您的建议,则不应添加到答案中。
阿金(Joakim)

3
@Joakim我发布新的“答案”没有问题-只是在适当的地方
注明

3
与公认的答案相比,这种方法的效果如何?
MaxCore

36

来自Django文档

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

33

您可以为此创建通用函数。

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,否则将返回Content条目。

注意:如果为name =“ baby”返回的条目超过一个,则会引发MultipleObjectsReturned异常



14

为了使事情变得更容易,以下是我编写的代码的片段,这些代码基于来自此处精彩答复的输入:

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()


7
同样,不要忘记导入:从django.core.exceptions导入ObjectDoesNotExist
Moti Radomski

13

您可以使用exists过滤器:

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

如果您只想知道它是否存在,则只是一个选择


4
如果存在,那将导致额外的数据库调用。这不是一个好主意
Christoffer,2015年

@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().
Anupam '18年

2
@克里斯托弗我认为你是对的。我现在再次阅读问题,OP实际上希望返回实际的对象。因此在获取对象之前exists()将与with if子句一起使用,从而导致对数据库的双重打击。我仍然会保留评论,以免对他人有所帮助。
阿努帕姆

7

在视图中的不同点处理异常可能真的很麻烦。.如何在models.py文件中定义自定义模型管理器,如

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

然后将其包含在内容模型类中

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

这样可以很容易地在视图中处理,即

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)使其对我有用。并不是说答案有什么问题,这只是对尝试在更高版本(或任何其他使它无法使用的版本)中使用的人的一个提示。
skagzilla

7

这是您可能不想重新实现的那些烦人的功能之一:

from annoying.functions import get_object_or_None
#...
user = get_object_or_None(Content, name="baby")

我检查了的代码,get_object_or_None但发现MultipleObjectsReturned如果有多个对象,它仍然会升高。因此,用户可能会考虑使用try-except(该功能本身已经具有try-except)。
John Pang

4

如果您想要一个不涉及异常处理,条件语句或Django 1.6+要求的简单的单行解决方案,请执行以下操作:

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

4

我认为使用它不是一个坏主意 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.")

您可以在Django在线文档中阅读有关get_object_or_404()的更多信息。


2

从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

这是helper函数的一种变体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`'s 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

可以通过两种方式使用它,例如:

  1. obj = get_unique_or_none(Model, **kwargs) 如前所述
  2. obj = get_unique_or_none(Model, parent.children, **kwargs)

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

我们可以使用Django内置异常,该异常附加到名为的模型上.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

0

这是Django的get_object_or_404的模仿,只是该方法返回None。当我们必须使用only()查询仅检索某些字段时,这非常有用。此方法可以接受模型或查询集。

from django.shortcuts import _get_queryset


def get_object_or_none(klass, *args, **kwargs):
    """
    Use get() to return an object, or return None if object
    does not exist.
    klass may be a Model, Manager, or QuerySet object. All other passed
    arguments and keyword arguments are used in the get() query.
    Like with QuerySet.get(), MultipleObjectsReturned is raised if more than
    one object is found.
    """
    queryset = _get_queryset(klass)
    if not hasattr(queryset, 'get'):
        klass__name = klass.__name__ if isinstance(klass, type) else klass.__class__.__name__
        raise ValueError(
            "First argument to get_object_or_none() must be a Model, Manager, "
            "or QuerySet, not '%s'." % klass__name
        )
    try:
        return queryset.get(*args, **kwargs)
    except queryset.model.DoesNotExist:
        return None

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.