2番目のモデルのエントリを除くdjangoクエリセット
-
08-07-2019 - |
質問
小さな語彙クイズアプリを作成していますが、単語の基本的なモデルは次のとおりです。
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にまだないWordに制限することです。
したがって、一般的に言えば、Wordのクエリセットに対して何らかの種類の.exclude()を実行したいと思いますが、WordLearnedテーブルのメンバーシップに基づいて除外する必要があります。これを行う良い方法はありますか?クエリセットを結合することに関する多くの参照を見つけましたが、これを行う方法に関する良いものを見つけることができませんでした(おそらく、検索するための適切な用語を知らないだけです)。
学習済み、作業中、または未学習を示すために各Wordのフラグを使用したくないのは、最終的にこれがマルチユーザーアプリになり、すべてのユーザーにフラグを付けたくないためです。 。したがって、各セットに対して複数のテーブルを使用する方が良いと考えました。
すべてのアドバイスを歓迎します。
解決
まず、スタイルに関するいくつかのメモ。
外部キーフィールドの前に id _
を付ける必要はありません。 DjangoがこれらのFKのために作成する基礎となるデータベースフィールドには、とにかく _id
のサフィックスが付いているため、dbで id_word_id
のようなものを取得します。 「word」、「student」などのフィールドを呼び出すだけで、コードがより明確になります。
また、各モデルに id
自動フィールドを指定する必要はありません。これらは自動的に作成されるため、他の名前を呼び出す必要がある場合にのみ指定する必要があります。同様に、メタも db_table
を指定する必要はありません。これも自動的に行われます。
最後に、Unicodeメソッドのフィールドで __ unicode __
を呼び出す必要はありません。文字列の補間は自動的にそれを行います、そして再びそれを省くことはあなたのコードをずっと読みやすくします。 (本当に明示的に行いたい場合は、少なくとも unicode(self.word)
フォームを使用してください。)
とにかく、あなたの実際の質問に。クエリセット自体を「結合」することはできません-モデル間クエリを実行する通常の方法は、あるモデルから別のモデルへの外部キーを持つことです。あなたはこれをできました:
words_to_work_on = Word.objects.exclude(WordLearned.objects.filter(student=user))
内部でサブクエリを実行して、現在のユーザーのすべてのWordLearnedオブジェクトを取得し、返された単語のリストからそれらを除外します。
ただし、特にマルチユーザーアプリの将来の要件を念頭に置いて、テーブルを再構築する必要があると思います。必要なのは、特定の生徒のWordのステータスをキャプチャする中間テーブルを使用した、Wordと生徒の間のManyToMany関係です。そうすれば、基本的に重複している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)