【问题标题】:Better way to check if a object is present in Django ORM among Get/Filter?在 Get/Filter 中检查对象是否存在于 Django ORM 中的更好方法?
【发布时间】:2017-12-22 19:23:51
【问题描述】:

让我们考虑一下我正在尝试查找主键为 20 的用户是否存在?我可以通过两种方式做到这一点。

第一个:

try:
   user = User.objects.get(pk=20)
except User.DoesNotExist:
   handle_non_existent_user()

另一种方式可能是:

users = User.objects.filter(pk=20)
if not users.exists():
   handle_non_existent_user()

检查存在的更好方法是什么?

这可能与此有关:What is the best way to check if data is present in django? 但是,由于指定的示例没有模型查询集的参考,人们更喜欢第一种方法。

也在以下问题的答案中:what is the right way to validate if an object exists in a django view without returning 404? 它主要是基于,因为我们没有得到相关对象的引用。

【问题讨论】:

  • exists() 存在仅出于一个原因。请使用它。在某些情况下,例如当您计划评估查询集时,请使用 bool(queryset) 而不是 exists() (from the docs)

标签: python mysql django django-models django-views


【解决方案1】:

在我看来,第一个是最好的方法,因为如果我忘记检查用户是否存在,即使我不使用try / except 子句,它也会给我一个错误。

另外,get 专门用于仅获得一件物品。

【讨论】:

    【解决方案2】:

    TLDR: 对于您几乎总是确定对象是 db 的情况,最好使用 try:get 对于对象不存在的可能性为 50% 的情况,那么它会更好使用if:filter.exists

    这真的取决于代码上下文。例如有if语句优于try/exceptUsing try vs if in python的情况

    所以对于您的问题,Difference between Django's filter() and get() methods 是相同的。 get 下面的方法调用filter

    https://github.com/django/django/blob/stable/1.11.x/django/db/models/query.py#L366

    def get(self, *args, **kwargs):
        """
        Performs the query and returns a single object matching the given
        keyword arguments.
        """
        clone = self.filter(*args, **kwargs)
        if self.query.can_filter() and not self.query.distinct_fields:
            clone = clone.order_by()
        num = len(clone)
        if num == 1:
            return clone._result_cache[0]
        if not num:
            raise self.model.DoesNotExist(
                "%s matching query does not exist." %
                self.model._meta.object_name
            )
        raise self.model.MultipleObjectsReturned(
            "get() returned more than one %s -- it returned %s!" %
            (self.model._meta.object_name, num)
        )
    

    因此,如果您将filterexists 一起使用。它将执行几乎相同的代码,因为下面的 exists 会这样做

    def exists(self):
        if self._result_cache is None:
            return self.query.has_results(using=self.db)
        return bool(self._result_cache)
    

    如您所见,filter.exists 将执行更少的代码并且应该更快地工作,但它不会返回一个对象。

    【讨论】: