0

私は小さな語彙クイズ アプリを作成しています。単語の基本モデルは次のとおりです。

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 にフラグを使用したくありません。したがって、各セットに複数のテーブルが適していると考えました。

すべてのアドバイスをいただければ幸いです。

4

1 に答える 1

8

Firstly, a couple of notes about style.

There's no need to prefix the foreign key fields with id_. The underlying database field that Django creates for those FKs are suffixed with _id anyway, so you'll get something like id_word_id in the db. It'll make your code much clearer if you just call the fields 'word', 'student', etc.

Also, there's no need to specify the id autofields in each model. They are created automatically, and you should only specify them if you need to call them something else. Similarly, no need to specify db_table in your Meta, as this is also done automatically.

Finally, no need to call __unicode__ on the fields in your unicode method. The string interpolation will do that automatically, and again leaving it out will make your code much easier to read. (If you really want to do it explicitly, at least use the unicode(self.word) form.)

Anyway, on to your actual question. You can't 'join' querysets as such - the normal way to do a cross-model query is to have a foreignkey from one model to the other. You could do this:

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

which under the hood will do a subquery to get all the WordLearned objects for the current user and exclude them from the list of words returned.

However, and especially bearing in mind your future requirement for a multiuser app, I think you should restructure your tables. What you want is a ManyToMany relationship between Word and Student, with an intermediary table capturing the status of a Word for a particular Student. That way you can get rid of the WordToWorkOn and WordLearned tables, which are basically duplicates.

Something like:

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()

Now you can get all the words to learn for a particular student:

words_to_learn = Word.objects.filter(studentword__student=student, studentword__learned=False)
于 2009-10-28T15:52:00.390 に答える