【发布时间】:2012-05-03 15:55:02
【问题描述】:
我正在尝试优化 Django 应用程序的数据库查询。这是一个简化的示例:
class Label(models.Model):
name = models.CharField(max_length=200)
# ... many other fields ...
class Thing(models.Model):
name = models.CharField(max_length=200)
labels = models.ManyToManyField(Label)
我有一个函数可以获取所有Labels 和Things 并将它们放入JSON 数据结构中,其中Things 使用它们的ids(主键)引用Labels .像这样的:
{
'labels': [
{ 'id': 123, 'name': 'label foo' },
...
],
'things': [
{ 'id': 45, 'name': 'thing bar', 'labels': [ 123, ... ] },
...
]
}
使用 Django 获取这种数据结构的最有效方法是什么?假设我有 L Labels 和 T Things,平均 Thing 有 x Labels。
方法一:
data = {}
data['labels'] = [model_to_dict(label) for label in Label.objects.all()]
data['things'] = [model_to_dict(thing) for thing in Thing.objects.all()]
这会产生 (1 + 1 + T) 次数据库查询,因为 model_to_dict(thing) 需要单独获取每个 Thing 的 Labels。
方法二:
data = {}
data['labels'] = [model_to_dict(label) for label in Label.objects.all()]
data['things'] = [model_to_dict(thing) for thing in
Thing.objects.prefetch_related('labels').all()]
这只会进行 (1 + 1 + 1) 次数据库查询,因为现在提取的 Things 在单个附加查询中预取了它们的 Labels。
这仍然不能令人满意。prefetch_related('labels') 将获取相同Label 的许多副本,而我只需要它们的ids。有没有办法只预取Labels 的ids?我试过prefetch_related('labels__id'),但没用。我还担心因为 T 很大(数百个),prefetch_related('labels') 会导致 SQL 查询带有很大的IN 子句。 L 要小得多(
方法三:
data = {}
data['labels'] = [model_to_dict(label) for label in
Label.objects.prefetch_related('thing_set').all()]
things = list(Thing.objects.all())
# plug in label ids by hand, and also fetch things that have zero labels
# somehow
这会导致更小的IN 子句,但仍然不能令人满意,因为如果Thing 有多个Labels,prefetch_related('thing_set') 会获取重复的Things。
总结:
Label 和 Thing 由 ManyToManyField 连接。无论如何,我正在获取 all Labels 和 Things。那么如何有效地获取他们的多对多关系呢?
【问题讨论】:
-
也许尝试使用m2m的中间模型? DB 方案和其他任何东西都将保持不变,但您只能
fetch_related这个模型并从中获取标签的 ID。如果您将它与through参数链接到 M2M,则某些方法(如add())将被破坏,但您可以手动为其提供db_table并且不要触摸 m2m 字段,所以它应该可以工作。 -
感谢@ilvar,您的评论让我得到了下面的答案。
标签: python database django optimization django-queryset