在django

时间:2016-06-30 01:03:59

标签: django django-models

我在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中绕过它。

3 个答案:

答案 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

的ID

第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那样好,因为它将需要为每一行执行子查询。