我有一个相当复杂的数据库,由一个模型Bar
组成,它可能有几个Spam
,由ForeignKey关系实现(每个Spam
知道哪个Bar
它属于)。另一个模型Foo
恰好有两个Bars
。这是models.py
的相关位:
class Bar(models.Model):
g = models.IntegerField()
...
class Spam(models.Model):
name = models.CharField(max_lendth=20)
val = models.CharField(max_length=10)
bar = models.ForeignKey(bar)
class Foo(models.Model):
bar1 = models.ForeignKey(Bar, related_name='foo_bar_2')
bar2 = models.ForeignKey(Bar, related_name='foo_bar_1')
我想知道哪个Foo
有一对特定的bar
,其中每一个都由Spams
的一组标识。获得与bars
匹配的两组很好并且速度相当快:
def get_bars_queryset(spam_tuples):
bars = Bar.objects
for name, val in spam_tuples:
bars = bars.filter(spam__name=name, spam__val=val)
return bars
bars1 = get_bars_queryset(spam_tuples=[('eggs', '5'), ('bacon', 'yes')])
bars2 = get_bars_queryset(spam_tuples=[('eggs', '1'), ('toast': '4'), ('milk', '2')])
但是,当我尝试以明显的方式检索我想要的Foo
时:
foos = Foo.objects.filter(bar1__in=bars1, bar2__in=bars2)
表现非常糟糕。事实证明,分别检索两组Foo
并找到它们的联合要快得多:
foos1 = Foo.objects.filter(bar1__in=bars1)
foos2 = Foo.objects.filter(bar2__in=bars2)
foo_bar1_ids = set(foos1.values_list('pk', flat=True))
foo_bar2_ids = set(foos2.values_list('pk', flat=True))
foo_id = foo_bar1_ids & foo_bar2_ids
foos = Foo.objects.filter(pk__in=foo_id)
我猜这是因为原生的Django方法没有有效地加入我的表。我怎么强迫它?
答案 0 :(得分:0)
好吧,如果它对其他人有帮助,解决办法是强制评估bar
查询集:
bars1 = list(get_bars_queryset(spam_tuples=[('eggs', '5'), ('bacon', 'yes')]))
bars2 = list(get_bars_queryset(spam_tuples=[('eggs', '1'), ('toast': '4'),
('milk', '2')]))
在使用过滤Foos
之前
foos = Foo.objects.filter(bar1__in=bars1, bar2__in=bars2)
这在文档(性能注意事项)中暗示:https://docs.djangoproject.com/en/1.7/ref/models/querysets/#in特别是MySQL的一个问题。感谢@ Minh-Hung Nguyen将我指向正确的方向。