如何在Django 1.9中表达sqlite LEFT OUTER JOIN?

时间:2016-05-04 10:24:33

标签: django django-models django-orm

我正在尝试为特定用户选择多个Content对象及其相关Vote值,如果Content对象没有相关{{} {1}}该用户的值。 (简化)模型看起来像这样

Vote

在SQLite中,这是一个简单的LEFT OUTER JOIN,它将字段class Content(models.Model): content_type = models.CharField(max_length=1, choices=choices, null=False) points = models.PositiveIntegerField(null=False, default=0) class Vote(models.Model): user = models.ForeignKey(User, related_name='votes', null=False) content = models.ForeignKey(Content, related_name='votes', null=False) value = models.SmallIntegerField(null=False) # either 1 or -1 is_upvote附加到is_downvote个对象,具体取决于它找到的Content值。

Vote

有没有办法在Django中表达这个?我发现this answer from 3 years ago建议进行两次单独的查找,然后在Python中加入它。从那时起Django ORM发生了变化吗?

在Python中加入它看起来有点像

SELECT c.id, c.points, 
    COALESCE((v.value == 1), 0) AS is_upvote, 
    COALESCE((v.value == -1), 0) AS is_downvote 
FROM pgm4app_content c 
LEFT OUTER JOIN pgm4app_vote v ON v.content_id = c.id AND v.user_id=1 
WHERE c.content_type='q';

1 个答案:

答案 0 :(得分:2)

是的!您可以在较新版本的Django中通过ORM执行此操作 - 请参阅documentation on conditional expressions

from django.db.models import IntegerField, Case, When, Sum, Q

Content.objects.all().annotate(
    num_votes=Sum(Case(
            When(Q(votes__user__pk=1) & Q(votes__value=1), then=1),
            When(Q(votes__user__pk=1) & Q(votes__value=-1), then=-1),
            default=0,
            output_field=IntegerField()
            )
        )
    )

生成的SQL查询类似于:

SELECT "myapp_content"."id", "myapp_content"."points", 
SUM(CASE WHEN ("myapp_vote"."user_id" = 1 AND "myapp_vote"."value" = 1) THEN 1 
WHEN ("myapp_vote"."user_id" = 1 AND "myapp_vote"."value" = -1) THEN -1 ELSE 0 END) 
AS "num_votes" FROM "myapp_content" 
LEFT OUTER JOIN "myapp_vote" ON ("myapp_content"."id" = "myapp_vote"."content_id") 
GROUP BY "myapp_content"."id", "myapp_content"."points"

而不是两个属性is_upvoteis_downvote而是返回一个属性num_votes,它可以是01-1,具体取决于用户如何投票。这可以在Python中简单地转换为两个属性。

(我确信必须可以在数据库中生成两个属性,但我第一次尝试失败!)。