Django模板变量重复查询问题及优化方案咨询
Django关联查询与模板渲染性能优化问题
模型定义
class Model1(models.Model): model2 = models.ForeignKey(Model2, on_delete=models.CASCADE) class Model2(models.Model): model3 = models.ForeignKey(Model3, on_delete=models.CASCADE) class Model3(models.Model): code1 = models.CharField(max_length=150) code2 = models.CharField(max_length=150) code3 = models.CharField(max_length=150) code4 = models.CharField(max_length=150) code5 = models.CharField(max_length=150) code6 = models.CharField(max_length=150) code7 = models.CharField(max_length=150)
模板写法对比
第一种写法
<td> {{ model1.model2.model3.code1 }} </td> <td> {{ model1.model2.model3.code2 }} </td> <td> {{ model1.model2.model3.code3 }} </td> <td> {{ model1.model2.model3.code4 }} </td> <td> {{ model1.model2.model3.code5 }} </td> <td> {{ model1.model2.model3.code6 }} </td> <td> {{ model1.model2.model3.code7 }} </td>
第二种写法
{% with col=model1.model2.model3 %} <td> {{ col.code1 }} </td> <td> {{ col.code2 }} </td> <td> {{ col.code3 }} </td> <td> {{ col.code4 }} </td> <td> {{ col.code5 }} </td> <td> {{ col.code6 }} </td> <td> {{ col.code7 }} </td> {% endwith %}
技术问题
前提:视图中未使用select_related,当前数据量不足1000行暂未出现性能问题,但关注大数据量下的查询优化。
- 第一种写法中,Django是否会对
model1.model2.model3执行7次查询? - 第二种使用
{% with %}标签的写法,是否仅查询一次关联对象,进而提升模板渲染速度? - 除上述两种方式外,还有哪些可行的替代方案?
问题解答
1. 第一种写法的查询次数
不会执行7次查询。Django的ORM自带内存缓存机制:第一次访问model1.model2.model3时,会触发两次关联查询(先查对应Model2实例,再查关联的Model3实例),之后访问该Model3实例的其他字段时,直接从缓存读取,不会重复发起数据库请求。但如果是批量处理多个Model1实例,未加select_related会触发N+1查询问题(每个Model1实例都会额外发起两次关联查询)。
2. {% with %}标签的作用
是的。{% with %}标签会在模板渲染阶段先执行model1.model2.model3的关联查询,将结果缓存到变量col中,后续所有col.codeX的调用都直接使用缓存的对象,不会重复查询。单实例场景下,它和第一种写法的查询次数一致,但让代码更简洁,减少了重复链式调用的出错概率,可读性和维护性更优。
3. 其他优化方案
- 视图层使用
select_related:这是大数据量下最有效的优化手段,通过Model1.objects.select_related('model2__model3')一次性将Model1、Model2、Model3的数据用JOIN查询取出,彻底避免N+1查询问题,大幅降低数据库查询次数。 - 模型层封装关联属性:在Model1中定义属性提前获取关联的Model3对象,把关联逻辑封装在模型层:
模板中直接调用class Model1(models.Model): model2 = models.ForeignKey(Model2, on_delete=models.CASCADE) @property def model3_data(self): return self.model2.model3{{ model1.model3_data.code1 }},效果和{% with %}类似,但逻辑更内聚。 - 使用
prefetch_related:如果涉及反向关联或多对多关系(当前场景正向外键更适合select_related),prefetch_related会通过批量查询的方式获取关联数据,减少查询次数。 - 模板缓存片段:如果Model3的数据更新频率低,可使用
{% cache %}标签缓存整个关联字段的渲染片段,避免重复查询和渲染操作。
内容的提问来源于stack exchange,提问作者Ahmad Irfan
相关产品推荐
相关产品推荐

