Django 1.8 条件注释导致 INNER JOIN 而不是 LEFT OUTER JOIN
Django 1.8 conditional annotation results in INNER JOIN instead of LEFT OUTER JOIN
模特:
class Bar(GenericModel):
...
class Foo(GenericModel):
bar = models.ForeignKey(Bar, related_name='foo_bar')
查询:
bars = Bar.objects
.prefetch_related('foo_bar')
.annotate(sum_foo=Sum(
Case(
When(foo_bar__is_deleted=False, then='foo_bar__amount'),
default=Value(0),
output_field=IntegerField()
)
)
)
前者导致内部联接:SELECT ... FROM "bar" INNER JOIN "foo" ON ( "bar"."id" = "foo"."bar_id" ) ...
我打算获得的是一个 LEFT OUTER JOIN("bar" 个对象的完整列表,用 "foo.amount" 和注释,如果 "foo" 与 "bar" 相关则为 0不存在)而不是 INNER JOIN?是否可以不退回到原始 SQL?
这种方式似乎可以正常工作:
bars = Bar.objects
.prefetch_related('foo_bar')
.annotate(sum_foo=Sum(
Case(
When(Q(foo_bar__is_deleted=False) | Q(foo_bar__is_deleted=None),
then='foo_bar__amount'),
default=Value(0),
output_field=IntegerField()
)
),
)
这是一个 known bug, corrected in Django 1.8.3 (release notes).
如您所述,问题是正在创建 INNER JOIN
,在与 Foo
个对象没有对应关系时过滤掉 Bar
个对象。
使用1.8.3以上的Django版本即可解决问题。
模特:
class Bar(GenericModel):
...
class Foo(GenericModel):
bar = models.ForeignKey(Bar, related_name='foo_bar')
查询:
bars = Bar.objects
.prefetch_related('foo_bar')
.annotate(sum_foo=Sum(
Case(
When(foo_bar__is_deleted=False, then='foo_bar__amount'),
default=Value(0),
output_field=IntegerField()
)
)
)
前者导致内部联接:SELECT ... FROM "bar" INNER JOIN "foo" ON ( "bar"."id" = "foo"."bar_id" ) ...
我打算获得的是一个 LEFT OUTER JOIN("bar" 个对象的完整列表,用 "foo.amount" 和注释,如果 "foo" 与 "bar" 相关则为 0不存在)而不是 INNER JOIN?是否可以不退回到原始 SQL?
这种方式似乎可以正常工作:
bars = Bar.objects
.prefetch_related('foo_bar')
.annotate(sum_foo=Sum(
Case(
When(Q(foo_bar__is_deleted=False) | Q(foo_bar__is_deleted=None),
then='foo_bar__amount'),
default=Value(0),
output_field=IntegerField()
)
),
)
这是一个 known bug, corrected in Django 1.8.3 (release notes).
如您所述,问题是正在创建 INNER JOIN
,在与 Foo
个对象没有对应关系时过滤掉 Bar
个对象。
使用1.8.3以上的Django版本即可解决问题。