【发布时间】:2016-10-08 14:08:49
【问题描述】:
我必须呈现一个非常复杂的页面,其中包含来自与 ForeignKey 和 ManyToManyField 相关的 3 个不同表的大量数据...我能够做我想做的事,但性能很糟糕,我一直在努力寻找更好的方法...这里有详细的代码:
型号:
class CATSegmentCollection(models.Model):
theFile = models.ForeignKey('app_file.File', related_name='original_file')
segmentsMT = models.ManyToManyField('app_mt.MachineTransTable', related_name='segMT', blank=True,)
segmentsTM = models.ManyToManyField('app_tm.TMTable', related_name='segTM', blank=True, through='app_cat.TM_Source_quality',)
...
class TM_Source_quality(models.Model):
catSeg = models.ForeignKey('app_cat.CATSegmentCollection')
tmSeg = models.ForeignKey('app_tm.TMTable')
quality = models.IntegerField()
class MachineTransTable(models.Model):
mt = models.ForeignKey('app_mt.MT_available', blank=True, null=True, )
...
class TMTable(models.Model):
...
从这些模型中(我刚刚写了与我的问题相关的内容),我展示了与单个文件相关的所有 CATSegmentCollection 条目......连同其相关的 TM 和 MT 段。换句话说,CATSegmentCollection 中的每个条目都有 TMTable 表中的零个或多个 TM 段和 MachineTransTable 表中的零个或多个 MT 段。
这就是我在 ListView 中所做的(我使用 AjaxListView,因为我使用的是来自 django-el-pagination 的无限滚动分页):
class CatListView(LoginRequiredMixin, AjaxListView):
Model = CATSegmentCollection
template_name = 'app_cat/cat.html'
page_template='app_cat/cat_page.html'
def get_object(self, queryset=None):
obj = File.objects.get(id=self.kwargs['file_id'])
return obj
def get_queryset(self):
theFile = self.get_object()
return CATSegmentCollection.objects.filter(theFile=theFile).prefetch_related('segmentsMT').prefetch_related('segmentsTM').order_by('segment_order')
def get_context_data(self, **kwargs):
context = super(CatListView, self).get_context_data(**kwargs)
contextSegment = []
myCatCollection = self.get_queryset()
theFile = self.get_object()
context['file'] = theFile
for aSeg in myCatCollection:
contextTarget = []
if aSeg.segmentsTM.all():
for aTargetTM in aSeg.tm_source_quality_set.all():
percent_quality = ...
contextTarget.append( {
"source" : aTargetTM.tmSeg.source,
"target" : aTargetTM.tmSeg.target,
"quality" : str(percent_quality) + '%',
"origin" : "TM",
"orig_name" : aTargetTM.tmSeg.tm_client.name,
"table_id" : aTargetTM.tmSeg.id,
})
if aSeg.segmentsMT.all():
for aTargetMT in aSeg.segmentsMT.all():
contextTarget.append( {
"target" : aTargetMT.target,
"quality" : "",
"origin" : "MT",
"orig_name" : aTargetMT.mt.name,
"table_id" : aTargetMT.id
})
contextSegment.append( {
"id" : aSeg.id,
"order" : aSeg.segment_order,
"source" : aSeg.source,
"target" : contextTarget,
})
context['segments'] = contextSegment
return context
一切正常,但是:
- 我每次调用 aSeg.segmentsTM.all() 和 aSeg.segmentsMT.all() 时都会访问数据库,因为我猜预取不会阻止它...这会导致数百个重复查询
- 每次我从分页加载更多条目时,所有这些查询都会重复(换句话说......每次由于滚动而呈现更多条目时,都会请求完整的条目集......我也尝试使用lazy_paginate但是没有任何变化)
- 原则上,我在 get_context_data 中的所有逻辑(还有更多,但我只是介绍了基本代码)可以在模板中复制,只传递查询集...或者由客户端使用大量 jquery/javascript 代码但我不认为这样进行是个好主意......
所以我的问题是...我可以优化此代码以减少 DB 命中数和产生响应的时间吗?只是为了让您了解一个相对较小的文件(CATSegmentCollection 中有 300 个条目)在 6.5 秒内加载,330 个查询(超过 300 个重复)需要 0.4 秒。 DJDT时间分析给出了
domainLookup 273 (+0)
connect 273 (+0)
request 275 (+-1475922263356)
response 9217 (+-1475922272298)
domLoading 9225 (+-1475922272306)
有什么建议吗? 谢谢
【问题讨论】:
-
有什么方法可以只获取一次数据然后在 python 中处理链接?由于您需要对其进行迭代的逻辑,一些复杂的查询无法预取。
标签: django django-orm manytomanyfield django-pagination