我正在建立一个需要维护案例跟踪系统的服务.这是我们的模型:
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数据网格和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模型上做了一个类似的视图,它的完整显示仅使用一个查询完成,因为如果您只从给定模型中提取字段,则不会对每个附加列执行额外的数据库命中.
我们尝试的一些优化是:
django-orm-cache:似乎不适用于django 1.0.4
django-caching:这适用于缓存经常查询的模型
使用memcached查看级别缓存
编辑:使用select_related()可以通过不往返数据库来加速模板渲染,但似乎它使用每个外键的单个查询提前在原始查询集上跟随外键.似乎提前移动了多数据库查询.
但是有一些更深层次的问题,我们正在征求人群的智慧:
对于具有大量外键的模型,有效查询从外键获取属性的最佳方法是什么?
缓存依赖模型是使用上述ORM缓存系统的唯一方法吗?
或者这是一个标准的案例,超出ORM并需要使用连接滚动我们自己的自定义SQL查询,以尽可能有效地获得所需的数据网格输出?
引起缓存和外键问题的相关问题:
DB/performance:django模型的布局,很少多次引用它的父级, Django ORM:缓存和操作ForeignKey对象:
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 不处理多对多关系 - 尽管我认为您可以手动查询m2m的中间表,以便在不需要额外查询的情况下跟随这些FK,只要您可以启动查询集来自中间对象.也许这就是抓住你的东西?你只指定了FK关系,而不是m2ms,所以我给出了一个简单的例子.