我在Django中有以下模型(简化为简洁):
class DistinctWord(models.Model):
...
class Word(models.Model):
distinct_word = models.ForeignKey('DistinctWord', related_name='words')
...
class UserWord(models.Model):
distinct_word = models.ForeignKey(DistinctWord, related_name='user_words')
...
在单词中:DistinctWord是彼此派生的单词的根(例如,愚蠢,愚蠢,愚蠢),UserWord是用户的字典。因此,当用户将Word添加到他的字典时,他实际上添加了根词(以及所有相关词)。所以,当他要求查看/研究他的词典中的单词时,我必须带一个真实的单词(例如,第一个单词)。
也就是说,对于给定的UserWords查询集(比如uw
),我想检索该查询集中每一行的第一个Word(最好是一次或几次访问数据库,而不是一个每一行)。这将是一个简单的连接,在原始sql中分组和限制1,但我无法在Django中绕过它。
答案 0 :(得分:1)
如果给出uw
的查询集:[obj.words.first() for obj in uw]
答案 1 :(得分:1)
让:
uw # be a given queryset of UserWord's
dw # be a queryset of DistinctWords (will be derived from `uw`)
w # be a queryset of Words needed (will be derived from `dw`)
每个UserWord
都有一个DistinctWord
,每个DistinctWord
都有Word
个{(松散地表示为uw>dw<w
)。
以下是我的回答:
dw_id=uw.values_list('distinct_word_id', flat=True) # 1: get dw ids from uw
dw=DistinctWord.objects.filter(id__in=dw_id) # 2: get dw's
w_first_id=dw.annotate(first_word=Min('words')).values_list('first_word', flat=True)
# 3: find id of first word
w=Word.objects.filter(id__in=w_first_id) # 4: get first words
总结:第1行和第2行获得dw
,并且只需要1次访问数据库
第3行使用annotate
后跟values_list
来查找第一个相关Word
第4行从上一步生成的id中带来实际的Word对象。第3行和第4行应该是数据库的另一次访问,因为annotate
不是终端语句。
因此2次访问数据库(未测试)。
答案 2 :(得分:0)
您可以使用Subquery API来做到这一点:
from django.db.models.expressions import Subquery, OuterRef
first_word = Word.objects.filter(
distinct_word=OuterRef('distinct_word')
).order_by('pk').values('pk')[:1]
UserWord.objects.filter(
# whatever filters...
).annotate(
first_word=Subquery(first_word)
)
这将导致看起来像 的SQL:
SELECT user_word.*,
(SELECT word.id
FROM word
WHERE word.distinct_word_id = user_word.distinct_word_id
) AS first_word
FROM user_word
WHERE ...
这在postgres中的DISTINCT ON可能不如JOIN那样好,并且在GROUP BY上可能不如JOIN那样好,因为它将需要为每一行执行子查询。