我有一个网站,用户可以在其中查看电影列表,并为它们创建评论。
用户应该能够看到所有电影的列表。此外,如果他们已经评论了这部电影,他们应该能够看到他们给这部电影的分数。如果没有,则只显示电影而不显示乐谱。
他们根本不关心其他用户提供的分数。
考虑以下 models.py
from django.contrib.auth.models import User
from django.db import models
class Topic(models.Model):
name = models.TextField()
def __str__(self):
return self.name
class Record(models.Model):
user = models.ForeignKey(User)
topic = models.ForeignKey(Topic)
value = models.TextField()
class Meta:
unique_together = ("user", "topic")
我本质上想要的是这个
select * from bar_topic
left join (select topic_id as tid, value from bar_record where user_id = 1)
on tid = bar_topic.id
考虑以下 test.py
上下文:
from django.test import TestCase
from bar.models import *
from django.db.models import Q
class TestSuite(TestCase):
def setUp(self):
t1 = Topic.objects.create(name="A")
t2 = Topic.objects.create(name="B")
t3 = Topic.objects.create(name="C")
# 2 for Johnny
johnny = User.objects.create(username="Johnny")
johnny.record_set.create(topic=t1, value=1)
johnny.record_set.create(topic=t3, value=3)
# 3 for Mary
mary = User.objects.create(username="Mary")
mary.record_set.create(topic=t1, value=4)
mary.record_set.create(topic=t2, value=5)
mary.record_set.create(topic=t3, value=6)
def test_raw(self):
print('\nraw\n---')
with self.assertNumQueries(1):
topics = Topic.objects.raw('''
select * from bar_topic
left join (select topic_id as tid, value from bar_record where user_id = 1)
on tid = bar_topic.id
''')
for topic in topics:
print(topic, topic.value)
def test_orm(self):
print('\norm\n---')
with self.assertNumQueries(1):
topics = Topic.objects.filter(Q(record__user_id=1)).values_list('name', 'record__value')
for topic in topics:
print(*topic)
两个测试都应该打印完全相同的输出,但是,只有原始版本会吐出正确的结果表:
生的
---
一个 1
B 无
丙3
orm 反而返回这个
规范
---
一个 1
丙3
任何试图加入其余主题的尝试,那些没有来自用户“johnny”的评论的主题,都会导致以下结果:
orm
---
A 1
A 4
B 5
C 3
C 6
如何使用 Django ORM 完成原始查询的简单行为?
编辑:这种作品但看起来很差:
topics = Topic.objects.filter(record__user_id=1).values_list('name', 'record__value')
noned = Topic.objects.exclude(record__user_id=1).values_list('name')
对于链中的主题(主题,无):
...
编辑:这个效果好一点,但仍然不好:
topics = Topic.objects.filter(record__user_id=1).annotate(value=F('record__value'))
主题 |= Topic.objects.exclude(pk__in=topics)
规范
---
一个 1
乙 5
丙3
原文由 RodericDay 发布,翻译遵循 CC BY-SA 4.0 许可协议
首先,没有办法(atm Django 1.9.7) 完全 按照您的意愿 使用您发布的原始查询 的 Django 的 ORM 进行表示;但是,您可以通过以下方式获得相同的期望结果:
这里为第一个查询生成的 SQL:
##一些笔记
distinct
在此答案中使用了位置参数,仅适用于 PostgreSQL,atm。在文档中,您可以看到更多关于 条件表达式 的信息。