Django:从数据库中获取对象,如果没有匹配项,则为“无”


101

是否有任何Django函数可以让我从数据库中获取对象,如果没有匹配项,则为None?

现在我正在使用类似:

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

但这还不是很清楚,到处都是混乱的。


2
您知道如果不使用foo,就可以使用foo = foo [0]无
Visgean Skeloru 2012年

2
Python具有三元运算符,您不必使用布尔运算符。另外,len(foo)也是很糟糕的:“ 注意:如果您要做的就是确定集中的记录数,请不要在QuerySets上使用len()。使用SQL的SELECT COUNT在数据库级别处理计数会更有效。 (),而Django正是出于这个原因提供了count()方法。重写:foo = foo[0] if foo.exists() else None
mustafa.0x 2013年


1
@ mustafa.0x我认为这里不适用。在这里,检查计数将运行另一个查询。然后,我们将再次查询以获取实际数据。在这种情况下,过滤然后计数将更有意义...但比过滤并调用更有意义first():P
MicronXD 2014年

Answers:


88

在Django 1.6中,您可以使用first()Queryset方法。它返回查询集匹配的第一个对象,如果没有匹配的对象,则返回None。

用法:

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

19
这不是一个好答案,如果找到多个对象,则MultipleObjectsReturned()不会引发该对象,如此处所述。您可以在此处
sleepycal 2014年

25
@sleepycal我不同意。该first()工程完全按照它想。它返回查询结果中的第一个对象,并且不在乎是否找到多个结果。如果需要检查是否返回了多个对象,则应.get()改用。
Cesar Canassa 2014年

7
[编辑]正如OP所说,.get()不适合他的需要。@kaapstorm可以在下面找到此问题的正确答案,并且显然是更合适的答案。滥用filter()这种方式可能会导致意想不到的后果,这可能是OP可能没有意识到的(除非我在这里遗漏了一些东西)
sleepycal 2014年

1
@sleepycal这种方法会带来哪些意想不到的后果?
HorseloverFat 2014年

12
如果您的数据库约束不能正确地管理对象之间的唯一性,那么您可能会遇到一些极端情况,即您的逻辑希望只有一个对象(由first()选择),但实际上存在多个对象。通过使用get()而不是first()可以为您提供额外的保护层MultipleObjectsReturned()。如果预期返回的结果不会返回多个对象,则不应将其视为此类。有一个长的这场辩论在这里
sleepycal

139

有两种方法可以做到这一点;

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)

8
我不喜欢它在功能中使用异常的事实-在大多数语言中,这都是不好的做法。在python中如何?
pcv

18
这就是Python迭代的工作方式(提高StopIteration),这是该语言的核心部分。我也不是try / except功能的忠实拥护者,但是它似乎被认为是Pythonic。
Matt Luongo 2012年

4
@pcv:没有关于“大多数”语言的可靠常规知识。我想您会考虑使用某种特定的语言,但是在使用此类量词时要格外小心。异常可以像Python中的任何东西一样慢(或“足够快”)。回到问题-这是框架的一个缺点,他们没有提供任何queryset方法在1.6之前就可以做到这一点!但至于这种结构-只是笨拙。这不是“邪恶的”,因为您最喜欢的语言的一些教程作者是这样说的。尝试使用google:“要求宽恕而不是允许”。
Tomasz Gandor

@TomaszGandor:是的,无论您喜欢的语言教程怎么说,它都笨拙且难以阅读。当我看到异常时,我认为正在发生一些非标准的事情。可读性很重要。但是我同意,当没有其他合理的选择时,您应该坚持下去,不会有任何不好的事情发生。
pcv 2013年

@pcv该get方法不应该返回None,因此可以使用异常。您应该在确定对象将存在/对象“应该”放置在其中的实例中使用它。同样使用这样的异常也被认为是“好的”,因为它很有意义。您想要一个get具有不同合同的合同,因此您要捕获异常并抑制它,这就是您要执行的更改。
Dan Passaro 2013年

83

要将一些示例代码添加到sorki的答案中(我将其添加为评论,但这是我的第一篇文章,并且我没有足够的声誉来发表评论),我实现了一个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')

这非常优雅。
NotSimon 2014年

13

您也可以尝试使用django烦人(它还有另一个有用的功能!)

用以下命令安装:

pip install django-annoying

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

9

给Foo 自定义经理。这很容易-只需将代码放入自定义管理器的函数中,在模型中设置自定义管理器,然后使用即可调用Foo.objects.your_new_func(...)

如果您需要通用函数(不仅要在自定义管理器中使用它,还可以在任何模型上使用它),请编写自己的函数并将其放在python路径上并导入,而不是再麻烦了。


4

无论是通过管理器还是通过通用函数执行操作,您都可能希望在TRY语句中捕获“ MultipleObjectsReturned”,因为如果kwarg检索了多个对象,则get()函数将引发此问题。

基于通用功能:

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

这似乎捕捉到了其他答案的所有优点。好人:)
爱德华·纽厄尔

@emispowder,如果我不想返回None,我想返回显示在SAME页面上的警告消息,如“无匹配数据”怎么办?
海伦娜

0

这是helper函数的一种变体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`'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(*args, **kwargs)
    except model.DoesNotExist:
        return None

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

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

-1

我认为在大多数情况下,您可以使用:

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

仅在不关键要在Foo表中添加新条目的情况下(其他列将具有None / default值)

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.