Django ManyToMany 模板渲染与性能问题

3 投票
2 回答
2450 浏览
提问于 2025-04-15 12:53

我有一个Django模型,它里面有一种多对多的关系,具体是这样的,

class MyModel(models.Model):
  name = ..
  refby = models.ManyToManyField(MyModel2)
  ..

class MyModel2(..):
  name = ..
  date = ..

我需要在我的模板中展示所有与mymodel相关的mymodel2对象。目前,我的做法是这样的,

{% for i in mymodel_obj_list %}
  {{i.name}}
  {% for m in i.refby.all|dictsortreversed:"date"|slice:"3" %}
    {{.. }}
  {% endfor %}
  <div> <!--This div toggles hidden/visible, shows next 12-->
   {% for n in i.refby.all|dictsortreversed:"date"|slice:"3:15" %}
     {{.. }}
   {% endfor %}
  </div>
{% endfor %}

正如代码所示,我只想显示最新的3个mymodel2对象,并且按照日期倒序排列,虽然实际上会加载接下来的12个。

这样做效率会很低吗?(考虑到refby.all的结果可能有几百个,而“mymodel_obj_list”的总结果也是几百个 - 我在这里使用了分页器)。

那么,预先计算这些refby并将它们渲染到模板的最佳方法是什么呢?我应该在视图中进行排序和计算,然后再传递过去吗?我不太确定这样做是否能保持我的分页功能。

视图代码大致是这样的,

obj_list = Table.objects.filter(..) # Few 100 records
pl = CustomPaginatorClass(obj_list...)

然后我将pl作为mymodel_obj_list传递给页面。

谢谢!

2 个回答

0

我应该在视图中进行排序和计算,然后再传递吗?

是的,绝对应该。

这其实不是性能的问题(因为Django的查询集是懒加载的,我觉得最终的性能在这两种情况下可能差不多),而是代码组织的问题。
模板不应该包含任何业务逻辑,只需要负责展示内容。当然,有时候这个原则会被打破,但一般来说,你应该尽量朝这个方向努力。

4

我假设 mymodel_obj_list 是一个查询集(QuerySet)。在循环中你访问了一个外键字段,这意味着默认情况下,Django 会一个一个地查找每个对象的引用。当你要显示很多行数据时,这样做会非常慢。

你可以在查询集上调用 select_related,这样可以提前把所有这些外键字段都拉取进来。

https://docs.djangoproject.com/en/dev/ref/models/querysets/#select-related

撰写回答