python 当我访问一个对象的属性时,Django会访问数据库吗?

4ktjp1zp  于 2023-05-05  发布在  Python
关注(0)|答案(2)|浏览(146)

下面的代码是否会在每次调用www.example.com时查询数据库question.name?

class Question(models.Model):
    name=models.CharField()
    test=models.ForeignKey(Test)

questions = Question.objects.filter(test=some_test)
for question in questions:
    question_name = question.name

我的视图接受一组问题和用户响应,并对工作表进行评分。我的目标是在不返回数据库的情况下执行所有评分,然后使用后台任务保存比较结果(字典),如果需要更长时间也可以。我想排除所有不必要的数据库匹配。

c0vxltue

c0vxltue1#

好吧,你的问题不清楚,但有一件事是肯定的,至少有一个命中数据库将是必需的。事实上,在代码示例中,您使用:

question_name = question.name

但是如果question不是Question模型的示例,我不认为这是可行的。
因此,考虑到您在创建示例的查询中对数据库的初始命中,question_name可以根据需要经常重用,而不必访问数据库。这是数据库中一行的静态示例。

编辑

现在对于您的编辑,它不是相同的行为。为了正确理解幕后发生的事情,你需要理解“懒惰”的概念。事实上,由于Django中的查询集是懒惰的,这行:

questions = Question.objects.filter(test=some_test)

不会在数据库里找到匹配的QuerySets代表SQL查询集。因此,在这一点上,由于您没有要求从数据库中获取某些内容,因此实际上尚未对其进行评估。这是我们想要的行为,因为它确保了数据库只在需要的时候被访问。正如文档所说,这里是查询集被评估的情况:

迭代

QuerySet是可迭代的,它在您第一次迭代它时执行其数据库查询。

切片

对一个未求值的QuerySet进行切片通常会返回另一个未求值的QuerySet,但是如果你使用切片语法的“step”参数,Django会执行数据库查询,并返回一个列表。对已计算的QuerySet进行切片也会返回一个列表。

筛选/缓存

repr()。当您对QuerySet调用repr()时,它将被计算。这在Python交互式解释器中是为了方便起见,因此当交互式使用API时,您可以立即看到结果。
len()。QuerySet在调用len()时计算。如您所料,这将返回结果列表的长度。
list()。通过调用list()强制计算QuerySet
bool()。在布尔上下文中测试QuerySet,例如使用bool()、or、and或if语句将导致执行查询
所以,正如你所看到的,情况1适用于你的特殊情况。迭代将导致数据库上的命中。

bjp0bcyl

bjp0bcyl2#

很好的问题,我也一直在寻找答案。从我读到的:当你开始迭代queryset时,所有与queryset匹配的行都将从数据库中获取并转换为Django模型。这被称为evaluationsource)。
一旦使用任何技术对queryset进行了计算-无论是迭代或强制转换为list/set,还是在if语句中使用它,以及here描述的其他技术,再次迭代它或使用它的属性都不会触发另一个查询/数据库命中,因为它已经被缓存了。除非-它是一个外键属性(前向或后向关系字段),在这种情况下,还可以使用select_relatedprefetch_related来缓存这些字段,以便访问这些字段不会导致数据库命中(source)。
简而言之,一旦计算了queryset,它的属性被检索,每个对象被缓存,用点运算符获取这些不会导致数据库命中,因为它是从该高速缓存中检索的,而不是从数据库中检索的,但是如果它s是一个外键属性,您没有按照定义缓存它,比如说使用cached_property装饰器,或者在使用select_related检索查询集时,或者prefetch_related,访问该属性将导致数据库命中(部分源)。

相关问题