为什么Get和Filter会给出不同的结果? (Django的)

时间:2018-05-18 17:17:38

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

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

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'

就好像过滤器无法跟踪对象之间的多种关系。

为什么会这样?

2 个答案:

答案 0 :(得分:2)

Bacause filter()返回queryset(多个学生)。但professors是单个学生实例的属性。您可以first()filter()一起使用以获取单个对象:

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

答案 1 :(得分:2)

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

Django&#39; .get(..)

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

Model.objects.get(..)

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

{em>热切地评估.get(..)函数:我们立即对数据库执行查询。如果数据库未返回任何条目,或两个或更多条目,则Model.DoesNotExistMultipleObjectsReturned例外将分别上升。

  

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

Student.objects.values().get(name='Mike')
     

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

Django&#39; .filter(..)

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

Django 非常评估此类.filter(..) 。这意味着默认情况下,Django将对数据库进行查询以检索条目。只有在结果查询集上调用示例len(..)或者对其进行迭代时,Django才会首先执行数据库查询,然后处理相应的结果。

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

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

然而,我们可以获取Professor个列表,其中包含一个或多个名为Student 'Mike'的{​​{1}}个:

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

过滤器永远不会引发Model.DoesNotExistMultipleObjectsReturned例外,因为完全允许使用包含多个项目的空QuerySetQuerySet s。