咨询Django通过外键访问关联模型实例时的数据库查询时机
Django ORM 链式外键访问的查询与缓存机制
嘿,这个问题问得挺到位的,正好能帮你搞清楚Django ORM的惰性加载和对象缓存逻辑!
咱们一步步拆解你给出的场景里的查询行为:
第一次查询:
house = House.objects.get(pk=1)
这一步只会查询House表,拿到House实例。此时它的window属性只是一个外键引用,并没有实际加载对应的Window数据——Django默认采用惰性加载策略,不会提前拉取关联对象,避免不必要的数据库开销。第二次查询:
window = house.window
当你第一次访问house.window时,Django会触发新的查询去拉取对应的Window实例。关键的点是:Django会把这个刚加载的Window实例缓存到House对象内部(具体存储在类似_window_cache的隐藏属性里),后续再访问house.window时,直接复用缓存的对象,不会再发起数据库查询。第三次查询:
glass = house.window.glass
这一步里,house.window直接调用的是之前缓存好的Window实例,完全不会触发新的Window查询!只会针对Glass表发起一次查询,拉取对应的Glass实例。
如果你想直观验证这个过程,可以借助Django的查询日志:
from django.db import connection # 第一次查询 house = House.objects.get(pk=1) print(len(connection.queries)) # 输出:1 # 第二次查询,加载Window并缓存 window = house.window print(len(connection.queries)) # 输出:2 # 仅查询Glass,复用缓存的Window glass = house.window.glass print(len(connection.queries)) # 输出:3
另外,如果你想减少查询次数,还可以用select_related提前预加载所有需要的关联对象,比如:
# 一次JOIN查询加载House、Window、Glass的所有数据 house = House.objects.select_related('window', 'window__glass').get(pk=1) print(len(connection.queries)) # 输出:1 # 后续链式访问不会触发任何新查询 glass = house.window.glass
内容的提问来源于stack exchange,提问作者smoquet
相关产品推荐
相关产品推荐

