建立一项需要维护案例跟踪系统的服务。这是我们的模型:
class Incident(models.Model):
title = models.CharField(max_length=128)
category = models.ForeignKey(Category)
status = models.ForeignKey(Status)
severity = models.ForeignKey(Severity)
owned_by = models.ForeignKey(User, related_name="owned_by", null=True, blank=True)
next_action = models.ForeignKey(IncidentAction)
created_date = models.DateTimeField()
created_by = models.ForeignKey(User, related_name="opened_by")
last_edit_date = models.DateTimeField(null=True, blank=True)
last_edit_by = models.ForeignKey(User, related_name="last_edit_by", null=True, blank=True)
closed_date = models.DateTimeField(null=True, blank=True)
closed_by = models.ForeignKey(User, related_name="Closed by", null=True, blank=True)
因为有很多外键被引入这个模型,所以它会产生有趣的sql查询。我们一直在使用djblets data grid和django调试工具栏作为试用版,并且每当我们为使用外键的视图添加新列时,每次查询的查询数量都很惊人,它基本上是这样的查询工作流程的类型:
#prepare the grid
select * from incident_table;
#render each row
for each row in incident table
for each column that is a foreign key select row from foreign table with id
它为每个尝试拉取外键属性的列执行额外的选择查询每行。
我认为这是django及其ORM关于从外键模型中提取属性以进行显示的普遍问题。作为测试,我删除了数据网格,只是为查询集做了一个简单的属性列表,并看到查询以类似的方式升级。
我们希望通过大量用户访问模型来扩大规模。作为比较,我在User模型上做了一个类似的视图,它的完整显示只用一个查询完成,因为如果你只从给定模型中提取字段,它不会为每个附加列做额外的数据库命中。
我们尝试的一些优化是:
但是有一些更深层次的问题我们正在征求群众的智慧:
引起缓存和外键问题的相关问题:
DB / performance: layout of django model that rarely refers to its parent more than once, Django ORM: caching and manipulating ForeignKey objects:
答案 0 :(得分:12)
select_related()是正确的解决方案;你错了它应该如何工作。如果你仍然在指定的FK上获得多个查询,我认为你没有正确使用select_related。快速记录Python会话(Studio在这里有一个FK到django.auth.user):
>>> from django.db import connection
>>> studios = Studio.objects.all().select_related('user')
>>> for studio in studios:
>>> print studio.user.email
>>>
email@notadomain.com
anotheremail@notadomain.com
>>> len(connection.queries)
1
因此,我得到了一个Studio对象列表(我的测试数据库中有2个),并在一个SQL查询中为每个对象获取了用户。如果没有select_related()调用,则需要三次查询。
请注意select_related doesn't handle many-to-many relationships - 虽然我认为您可以手动查询m2m的中间表,以便在不需要额外查询的情况下跟随这些FK,只要您可以从中启动查询集中间对象。也许这就是抓住你的东西?你只指定了FK关系,而不是m2ms,所以我给出了一个简单的例子。