【问题标题】:Why do Get and Filter give different results? (Django)为什么 Get 和 Filter 给出不同的结果? (姜戈)
【发布时间】:2018-10-29 04:37:24
【问题描述】:

我正在开发一个应用程序,学生可以在其中评估他们的老师。我有几个模型,但这个问题的重要模型是:

class Professor(models.Model):
    name = models.CharField(max_length=50,null=True)
    categories = models.ManyToManyField(Category, related_name='professors')
    def __str__(self):
        return self.name

class Student(models.Model):
    name = models.CharField(max_length=50,null=True)
    professors = models.ManyToManyField(Professor, related_name='students',through='Studentprofesor' )
    def __str__(self):
        return self.name

class Studentprofesor(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    professor =  models.ForeignKey(Professor, on_delete=models.CASCADE)
    tested = models.BooleanField(default=False)

据我所知,getfilter 之间的主要区别在于,当有多个对象具有我正在寻找的功能时,我无法使用 get。但除此之外,他们以类似的方式工作。 get 用于单个对象,filter 用于多个对象。

但是,在这种情况下,当我运行 getfilter 时会得到不同的结果。

如果我使用get:

Student.objects.get(name="Mike").professors.all()

我得到:

<QuerySet [<Professor: Tom>, <Professor: Jenn>]>

但是如果我使用filter:

Student.objects.filter(name="Mike").professors.all()

我得到:

AttributeError: 'QuerySet' object has no attribute 'professors'

好像过滤器无法遵循对象之间的多对多关系。

为什么会这样?

【问题讨论】:

  • 因为有多个。 .get(..)立即获取单个实体,而过滤器不过滤,除非您强制执行查询。

标签: django django-queryset django-orm django-filter


【解决方案1】:

.get(..).filter(..) 之间存在巨大差异。简而言之:.get(..) 获得满足给定条件的单个模型实例,而.filter(..) 过滤查询集并生成概念上包含满足给定条件的模型实例s (!) 的查询集。

Django 的.get(..)

.get 表示您的目标是检索恰好一个实例。所以这意味着如果你写:

Model.objects.get(..)

结果是一个Model 实例(假设有这样的实例)。因此,我们可以从该单个实体中获取属性(如.professors 等)。如果调用成功,我们可以保证: (1) 不存在满足过滤条件的多个对象; (2) 至少有一个元素符合过滤标准。所以输出总是是一个模型实例,而不是NoneQuerySet

.get(..) 函数被评估急切:我们立即对数据库执行查询。如果数据库没有返回条目,或者返回两个或更多条目,则分别引发 Model.DoesNotExistMultipleObjectsReturned 异常。

注意:由于.get(..) 急切地行动,在.get(..) 右侧添加过滤器等没有用(好吧,除非您在该实例上定义filter 函数,但是也不是一个好主意)。但是,您可以使用左侧的.values()values_listprefetch_related 等函数来更改输出类型(并预取某些部分)。例如:

Student.objects<b>.values()</b>.get(name='Mike')

将生成一个包含该实例值的字典。

Django 的.filter(..)

过滤另一方面过滤一个查询集。这意味着在我们过滤之后,查询集可能不再包含任何实例(如果过滤器过于严格)或两个或更多(如果过滤器太弱而无法固定到单个条目)。

Django 确实评估这样的.filter(..)热切。这意味着默认情况下 Django 将查询数据库以检索条目。仅当您在结果查询集上调用例如 len(..) 或对其进行迭代时,Django 才会首先执行数据库查询,然后处理相应的结果。

由于.filter(..) 的结果是另一个QuerySet,我们可以将操作链接在一起。例如,我们可以调用一个额外的.filter(..).exclude(..)values_list(..)QuerySet支持的任何其他函数。

由于结果不是模型实例,我们不能调用模型实例的属性。如果没有学生符合标准,Student.objects.filter(..).name 的结果应该是什么?或者如果有多个Students 匹配给定的约束呢?

但是,我们可以通过以下方式获取Professors 的列表,这些Students 教授一个或多个Students,名称为'Mike'

# professors with a student called Mike
Professors.objects.filter(students__name="Mike")

过滤器永远不会引发Model.DoesNotExistMultipleObjectsReturned 异常,因为它完全可以使用空的QuerySets 或QuerySets 处理多个项目。

【讨论】:

  • 有什么方法可以探索我用 get() 检索到的模型实例的元素吗?即使用 filter() 时的 values() 等价物
  • @Ale: 是的,您可以将.values(..) 放在链的前面,在这种情况下,您可以获得一个字典。
【解决方案2】:

因为filter() 返回查询集(多个学生)。但是professors 是单个学生实例的属性。您可以使用first()filter() 来获取单个对象:

Student.objects.filter(name="Mike").first().professors.all()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-15
    • 2017-04-29
    • 2021-12-07
    • 2020-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多