django queryset排除第二个模型中的条目

时间:2009-10-28 15:09:04

标签: django django-models django-queryset

我正在制作一个小词汇测验应用程序,一个单词的基本模型是:

class Word(models.Model):
    id = models.AutoField(primary_key=True)
    word = models.CharField(max_length=80)
    id_image = models.ForeignKey(Image)
    def __unicode__(self):
        return self.word
    class Meta:
        db_table = u'word'

我目前正在测试的单词模型是:

class WordToWorkOn(models.Model):
    id = models.AutoField(primary_key=True)
    id_student = models.ForeignKey(Student)
    id_word = models.ForeignKey(Word)
    level = models.IntegerField()
    def __unicode__(self):
        return u'%s %s' % (self.id_word.__unicode__(), self.id_student.__unicode__() )
    class Meta:
        db_table = u'word_to_work_on'

“水平”表示我学到了多少。我已经学过的一组单词有这个模型:

class WordLearned(models.Model):
    id = models.AutoField(primary_key=True)
    id_word = models.ForeignKey(Word, related_name='word_to_learn')
    id_student = models.ForeignKey(Student, related_name='student_learning_word')
    def __unicode__(self):
        return u'%s %s' % (self.id_word.__unicode__(), self.id_student.__unicode__() )
    class Meta:
        db_table = u'word_learned'

当WordToWorkOn上的查询集返回时结果太少(因为它们已经足够好地学习以便移入WordLearned并从WordToWorkOn中删除),我想找到要添加到其中的Word。我不知道一个好方法的部分是将它限制为尚未在WordLearned中的单词。

所以,一般来说,我想我想在一个单词的查询集上做某种类型的.exclude(),但它需要根据WordLearned表中的成员资格进行排除。有没有办法做到这一点?我发现很多关于加入查询集的参考资料,但是找不到一个关于如何做到这一点的好文章(可能只是不知道要搜索的正确术语)。

我不想在每个Word上使用一个标志来表示学习,处理或不学习,因为最终这将是一个多用户应用程序,我不希望每个用户都有标记。因此,我认为每组的多个表会更好。

感谢所有建议。

1 个答案:

答案 0 :(得分:7)

首先,关于风格的几个注释。

无需使用id_为外键字段添加前缀。 Django为这些FK创建的底层数据库字段无论如何都以_id为后缀,因此您将在db中获得类似id_word_id的内容。如果你只是调用字段'word','student'等,它会让你的代码更清晰。

此外,无需在每个模型中指定id个自动对象。它们是自动创建的,只有在需要调用其他内容时才应指定它们。同样,您无需在Meta中指定db_table,因为这也是自动完成的。

最后,无需在unicode方法的字段上调用__unicode__。字符串插值将自动执行,并再次将其删除将使您的代码更容易阅读。 (如果你真的想明确地这样做,至少要使用unicode(self.word)表格。)

无论如何,关于你的实际问题。您无法像这样“加入”查询集 - 执行跨模型查询的常规方法是从一个模型到另一个模型使用外键。你可以这样做:

words_to_work_on = Word.objects.exclude(WordLearned.objects.filter(student=user))
引擎盖下的

将执行子查询以获取当前用户的所有WordLearned对象,并将其从返回的单词列表中排除。

但是,特别是考虑到您对多用户应用程序的未来需求,我认为您应该重新构建表。你想要的是Word和Student之间的ManyToMany关系,中间表捕获特定学生的Word状态。这样你就可以摆脱WordToWorkOn和WordLearned表,这些表基本上都是重复的。

类似的东西:

class Word(models.Model):
    word = models.CharField(max_length=80)
    image = models.ForeignKey(Image)
    def __unicode__(self):
        return self.word

class Student(models.Model):
     ... name, etc ...
     words = models.ManyToManyField(Word, through='StudentWord')

class StudentWord(models.Model):
    word = models.ForeignKey(Word)
    student = models.ForeignKey(Student)
    level = models.IntegerField()
    learned = models.BooleanField()

现在,您可以获得为特定学生学习的所有单词:

words_to_learn = Word.objects.filter(studentword__student=student, studentword__learned=False)