我正在尝试为特定用户选择多个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';
答案 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_upvote
和is_downvote
而是返回一个属性num_votes
,它可以是0
,1
或-1
,具体取决于用户如何投票。这可以在Python中简单地转换为两个属性。
(我确信必须可以在数据库中生成两个属性,但我第一次尝试失败!)。