【问题标题】:django queryset excluding entries in second modeldjango queryset 不包括第二个模型中的条目
【发布时间】:2010-12-10 21:48:03
【问题描述】:

我正在做一个小词汇测验应用,一个单词的基本模型是这样的:

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 中删除),我想找一个词添加到它。我不知道一个好的方法是将其限制为 WordLearned 中尚未出现的单词。

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

我不想只在每个单词上使用标志来表示已学习、正在使用或未学习,因为最终这将是一个多用户应用程序,我不希望每个用户都有标志.因此,我认为每组有多个表会更好。

感谢所有建议。

【问题讨论】:

    标签: django django-models django-queryset


    【解决方案1】:

    首先,关于风格的几点说明。

    不需要在外键字段前面加上id_。无论如何,Django 为这些 FK 创建的底层数据库字段都以 _id 为后缀,因此您将在 db.xml 中获得类似 id_word_id 的内容。如果您只将字段称为“单词”、“学生”等,它会使您的代码更清晰。

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

    最后,无需在 unicode 方法中的字段上调用 ​​__unicode__。字符串插值将自动执行此操作,并且再次将其省略将使您的代码更易于阅读。 (如果你真的想明确地这样做,至少使用unicode(self.word) 表单。)

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

    words_to_work_on = Word.objects.exclude(WordLearned.objects.filter(student=user))
    

    在后台会执行一个子查询来获取当前用户的所有 WordLearned 对象,并将它们从返回的单词列表中排除。

    但是,特别是考虑到您未来对多用户应用程序的需求,我认为您应该重组您的表格。您需要的是 Word 和 Student 之间的多对多关系,其中包含一个捕获特定 Student 的 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)
    

    【讨论】:

    • 哇,三课合一。我将相应地修改我的数据结构,并在我实际实现它后回来检查这个答案是否正确(不是我真的怀疑它,只是原则上)。感谢所有的帮助。
    • 很好的解决方案,但是你怎么不能接受 Word 对象,StudentWord 表中没有什么?
    • 这里还有一些排除功能的错误,因此无法使用它来过滤 2 个或更多字段。 code.djangoproject.com/ticket/14645
    猜你喜欢
    • 2015-08-09
    • 1970-01-01
    • 1970-01-01
    • 2011-08-03
    • 2019-11-29
    • 2014-01-15
    • 2014-01-13
    • 2016-11-19
    • 2018-08-12
    相关资源
    最近更新 更多