【发布时间】:2016-10-29 20:06:31
【问题描述】:
我有一个网站,用户可以在其中查看电影列表并为它们创建评论。
用户应该能够看到所有电影的列表。此外,如果他们看过这部电影,他们应该能够看到他们给它的分数。如果没有,则电影只显示,不带乐谱。
他们根本不关心其他用户提供的分数。
考虑以下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 无 C 3
orm 会返回这个
orm --- 1 C 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')
对于链中的主题(主题,无):
...
编辑:这效果好一点,但仍然很糟糕:
主题 = Topic.objects.filter(record__user_id=1).annotate(value=F('record__value'))
主题 |= Topic.objects.exclude(pk__in=topics)
orm --- 1 乙 5 C 3
【问题讨论】:
-
这个问题似乎很相关:stackoverflow.com/questions/6500066/…
-
Record.objects.get(user=mary, topic=A)有什么问题? -
它不符合我的要求。它只会让我得到 Mary 写东西的记录,并且主题是 A。我想要所有的主题,而不仅仅是 A,而不仅仅是 Mary 写过评论的那些。
-
将来您可以在查询集上注释子查询:github.com/django/django/pull/6478
标签: python django django-models orm