【问题标题】:How do I get the object if it exists, or None if it does not exist in Django?如果对象存在,我如何获取它,或者如果它在 Django 中不存在,我如何获取它?
【发布时间】:2011-03-06 15:22:28
【问题描述】:

当我要求模型管理器获取对象时,它会在没有匹配对象时引发DoesNotExist

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

我怎样才能让go 代替None 而不是None

【问题讨论】:

    标签: python django django-queryset


    【解决方案1】:

    没有“内置”方法可以做到这一点。 Django 每次都会引发 DoesNotExist 异常。 在 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').

    【讨论】:

    • 很好地使用 SomeModel.DoesNotExist 而不是导入异常。
    • 这个解决方案有四行。对我来说,这太过分了。使用 django 1.6,您可以使用 SomeModel.objects.filter(foo='bar').first() 这将返回第一个匹配项,或者无。如果有多个实例,例如queryset.get(),它不会失败
    • 我认为过度使用异常来处理默认情况是不好的风格。是的,“请求宽恕比请求许可更容易”。但在我看来,仍然应该使用异常来处理异常。
    • 显式优于隐式。除非出于性能原因使用filter().first(),否则我认为例外是要走的路。
    • 如果您不在乎是否有多个,则使用 first() 只是一个好主意。否则,此解决方案会更好,因为如果您意外找到多个对象,它仍然会抛出异常,这通常是您希望在这种情况下发生的情况。
    【解决方案2】:

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

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

    【讨论】:

    • 在这种情况下,如果有多个匹配项,则不会引发错误。
    • 'FeroxTL' 您需要将此答案归功于 @guettli,因为他在您的帖子发布前一年对已接受的答案发表了评论。
    • @colminator 我宁愿说 guettli 应该知道,如果他想提高他的 stackoverflow 声誉,新答案不属于评论 :) FeroxTL 应该获得积分,因为他将隐藏的东西作为评论更多明确作为答案。我认为您的评论对 guettli 来说已经足够了,如果这是您的建议,则不应将其添加到答案中。
    • @Joakim 我对发布一个新的“答案”没有任何问题 - 只是为了在到期时给予信用:-)
    • 与接受的答案相比,这种方法的性能如何?
    【解决方案3】:

    From 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
    

    【讨论】:

      【解决方案4】:

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

      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

      您应该在数据模型上处理它以避免此类错误,但您可能更喜欢在运行时记录它,如下所示:

      def get_or_none(classmodel, **kwargs):
          try:
              return classmodel.objects.get(**kwargs)
          except classmodel.MultipleObjectsReturned as e:
              print('ERR====>', e)
      
          except classmodel.DoesNotExist:
              return None
      

      【讨论】:

      • 太棒了!如果是客户端错误: from django.shortcuts import get_object_or_404 否则是服务器端问题,所以 get_or_none 是最好的。
      【解决方案5】:

      你可以这样做:

      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

      【讨论】:

        【解决方案6】:

        为了让事情更简单,这里是我编写的代码的 sn-p,基于此处精彩回复的输入:

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

        【讨论】:

        • 另外,别忘了导入:from django.core.exceptions import ObjectDoesNotExist
        • 或者除了 self.model.DoesNotExist 来避免导入。
        【解决方案7】:

        您可以将exists 与过滤器一起使用:

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

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

        【讨论】:

        • 存在时会导致额外的数据库调用。不是个好主意
        • @Christoffer 不确定为什么会是额外的数据库调用。根据docsNote: 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().
        • @Christoffer 我认为你是对的。我现在再次阅读了这个问题,OP 实际上希望返回实际对象。所以exists() 将在获取对象之前与if 子句一起使用,从而对数据库造成双重打击。我仍然会保留评论以防它对其他人有所帮助。
        【解决方案8】:

        这是您可能不想重新实现的 annoying functions 之一:

        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)。
        【解决方案9】:

        在视图的不同点处理异常可能真的很麻烦..在 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
        

        【讨论】:

        • 我真的很喜欢这个解决方案,但是在使用 python 3.6 时无法让它正常工作。想留下一条说明,将 ContentManager 中的返回修改为 return self.get(**kwargs) 让它为我工作。并不是说答案有什么问题,只是给任何试图在更高版本中使用它的人(或其他任何阻止它为我工作的东西)的提示。
        【解决方案10】:

        也许你使用更好:

        User.objects.filter(username=admin_username).exists()
        

        【讨论】:

        • 这个问题是如果用户在这一行和实际检索到的行之间被删除。
        【解决方案11】:

        我觉得用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() 的更多信息。

        【讨论】:

          【解决方案12】:

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

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

          【讨论】:

            【解决方案13】:

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

            【讨论】:

              【解决方案14】:

              这是 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
              

              【讨论】:

                【解决方案15】:

                这是帮助函数的一个变体,它允许您有选择地传入 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)

                【讨论】:

                  【解决方案16】:

                  无一例外:

                  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 中使用异常存在一些争论。一方面,“请求宽恕比请求许可更容易”。虽然我同意这一点,但我认为应该保留一个例外,好吧,例外,并且“理想情况”应该运行而不会碰到一个。

                  【讨论】:

                    【解决方案17】:

                    我们可以使用 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
                    

                    【讨论】:

                      【解决方案18】:

                      我也面临同样的问题。当您想从模型中获取元素时,就像@Arthur Debert 的回答一样,每次都很难编写和阅读try-except。所以,我的解决方案是创建一个由模型继承的Getter 类:

                      class Getter:
                          @classmethod
                          def try_to_get(cls, *args, **kwargs):
                              try:
                                  return cls.objects.get(**kwargs)
                              except Exception as e:
                                  return None
                      
                      class MyActualModel(models.Model, Getter):
                          pk_id = models.AutoField(primary_key=True)
                          ...
                      

                      这样就可以得到MyActualModel或者None的实际元素了:

                      MyActualModel.try_to_get(pk_id=1)
                      

                      【讨论】:

                        【解决方案19】:

                        我使用 Django 2.2.16。这就是我解决这个问题的方法:

                        from typing import Any
                        
                        from django.core.exceptions import ObjectDoesNotExist
                        from django.db import models
                        from django.db.models.base import ModelBase
                        from django.db.models.manager import Manager
                        
                        
                        class SManager(Manager):
                            def get_if_exist(self, *args: Any, **kwargs: Any):
                                try:
                                    return self.get(*args, **kwargs)
                                except ObjectDoesNotExist:
                                    return None
                        
                        
                        class SModelBase(ModelBase):
                            def _prepare(cls):
                                manager = SManager()
                                manager.auto_created = True
                                cls.add_to_class("objects", manager)
                        
                                super()._prepare()
                        
                            class Meta:
                                abstract = True
                        
                        
                        class SModel(models.Model, metaclass=SModelBase):
                            managers = False
                        
                            class Meta:
                                abstract = True
                        

                        然后,在每个模型中,您只需要导入:

                        from custom.models import SModel
                        
                        
                        class SUser(SModel):
                            pass
                        

                        views,你可以这样调用:

                        SUser.objects.get_if_exist(id=1)
                        

                        【讨论】:

                          【解决方案20】:

                          切片怎么样?它将解析到一个限制 1。

                          go = Content.objects.filter(name="baby")[0]
                          

                          【讨论】:

                          • 是的,如果列表为空,您将获得IndexError。干得好
                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 2016-05-24
                          • 2019-06-14
                          • 1970-01-01
                          • 2021-09-08
                          • 2014-11-09
                          • 2013-02-06
                          • 2012-06-09
                          相关资源
                          最近更新 更多