【问题标题】:What's the best way to handle Django's objects.get?处理 Django 的 objects.get 的最佳方法是什么?
【发布时间】:2011-05-20 04:24:18
【问题描述】:

每当我这样做时:

thepost = Content.objects.get(name="test")

当什么都没找到时它总是抛出一个错误。 我该如何处理?

【问题讨论】:

  • 另见QuerySet.get
  • 这就是它应该工作的方式。你有什么问题?如何编写try 语句?

标签: python django


【解决方案1】:
try:
    thepost = Content.objects.get(name="test")
except Content.DoesNotExist:
    thepost = None

使用模型DoesNotExist异常

【讨论】:

  • 在这种情况下,异常是Content.DoesNotExist,如果您想在找不到对象的情况下继续(即不像我的示例中那样抛出404错误),此代码结构很有用。
【解决方案2】:

通常情况下,直接使用 Django 快捷函数get_object_or_404 代替 API 会更有用:

from django.shortcuts import get_object_or_404

thepost = get_object_or_404(Content, name='test')

很明显,如果找不到对象,这将引发 404 错误,如果成功,您的代码将继续。

【讨论】:

    【解决方案3】:

    捕获异常

    try:
        thepost = Content.objects.get(name="test")
    except Content.DoesNotExist:
        thepost = None
    

    或者您可以过滤,如果没有匹配项,它将返回一个空列表

    posts = Content.objects.filter(name="test")
    if posts:
        # do something with posts[0] and see if you want to raise error if post > 1
    

    【讨论】:

    • 为什么在 get() 只会引发异常时分配一个空的查询集? try/catch first 方法要好得多
    • 通常情况下,对正常的“工作流程”使用异常不是一个好习惯。在这个例子中,它相当容易理解,但如果你开始使用异常来控制应用程序,它就会很难维护/调试。
    • @mrmuggles 我不同意,异常可能是构建代码的一种非常好的方法,例如 for plugin in plugins; try; processed_text = plugin.process(text) ; except Exception1,: do something Exception 2; do something 没有更简单的替代方法,在这种特定情况下,它绝对是完美的,您会在访问 item 之前执行 dict.has_key 还是仅访问 item 并捕获 KeyError?
    • 使用异常来构建工作流是一种反模式 IMO。这是一个关于此的讨论:programmers.stackexchange.com/questions/189222/… 是的,您可以节省几行代码,但这并不是一个很好的维护解决方案。我会检查该项目是否存在,事实上我可能会将此代码包装在一个方法中。
    • @mrmuggles Python 社区支持 EAFP,即请求宽恕比请求许可更容易。这种风格在标准库和必须使用的大型 Python 第三方库中被广泛采用。这是我在 Google 上搜索到的关于该主题的随机博客文章:jeffknupp.com/blog/2013/02/06/…
    【解决方案4】:

    您还可以捕获一个通用的 DoesNotExist。根据http://docs.djangoproject.com/en/dev/ref/models/querysets/的文档

    from django.core.exceptions import ObjectDoesNotExist
    try:
        e = Entry.objects.get(id=3)
        b = Blog.objects.get(id=1)
    except ObjectDoesNotExist:
        print "Either the entry or blog doesn't exist."
    

    【讨论】:

      【解决方案5】:

      提出Http404 exception 效果很好:

      from django.http import Http404
      
      def detail(request, poll_id):
          try:
              p = Poll.objects.get(pk=poll_id)
          except Poll.DoesNotExist:
              raise Http404
          return render_to_response('polls/detail.html', {'poll': p})
      

      【讨论】:

      • 只有在 Poll 对象代表整个视图时才有效。如果您只是想查询将落在页面中间的对象,您不想为整个视图返回 404。
      【解决方案6】:

      在视图的不同点处理异常可能真的很麻烦..在 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 != None:
          # Do something
      else:
          # This post doesn't exist
      

      【讨论】:

        【解决方案7】:

        另一种写法:

        try:
            thepost = Content.objects.get(name="test")
        except Content.DoesNotExist:
            thepost = None
        

        很简单:

        thepost = Content.objects.filter(name="test").first()
        

        请注意,两者并不完全相同。管理器方法get 不仅会在您查询的没有记录的情况下引发异常,而且在找到多条记录时也会引发异常。当有多个记录时使用 first 可能会通过返回第一条记录以静默方式使您的业务逻辑失败。

        【讨论】:

        • 只是想插话 - 我发现使用 .get 而不是 .first 几乎总是更好,因为正如您所说,它可能会导致意想不到的结果。我唯一一次使用.first 是如果我使用通用外键并进行验证以确保最多只有一项。
        【解决方案8】:

        基本上有两种方法可以做到这一点。第一种方法更冗长,因为它不使用任何shortcuts

        from django.http import Http404
        from .models import Content    
        
        
        try:
            thepost = Content.objects.get(name="test")
        except Content.DoesNotExist:
            raise Http404("Content does not exist")
        

        现在由于这种操作(即获取一个对象或如果它不存在则引发 404)在 Web 开发中相当普遍,Django 提供了一个名为 get_object_or_404 的快捷方式,它将 get() 请求的对象或引发 @987654327 @以防万一找不到:

        from django.shortcuts import get_object_or_404
        from .models import Content
        
        
        thepost = get_object_or_404(Content, name="test")
        

        同样对于对象列表,您可以使用get_list_or_404() 函数,该函数利用filter() 而不是get(),如果返回的列表为空,Http404 将被提升。

        【讨论】:

          猜你喜欢
          • 2010-12-02
          • 2010-09-06
          • 2012-09-18
          • 2010-11-15
          • 2016-05-26
          • 2018-12-03
          • 1970-01-01
          • 1970-01-01
          • 2016-06-27
          相关资源
          最近更新 更多