набор запросов django, исключая записи во второй модели - PullRequest
0 голосов
/ 28 октября 2009

Я создаю небольшое приложение для определения словарного запаса, и базовая модель для слова такова:

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. Есть ли хороший способ сделать это? Я нахожу много ссылок на присоединение наборов запросов, но не могу найти хорошего о том, как это сделать (возможно, просто не знаю правильного термина для поиска).

Я не хочу просто использовать флаг в каждом Слове для обозначения изученного, работающего над ним или не усвоенного, потому что в конечном итоге это будет многопользовательское приложение, и я не хотел бы иметь флаги для каждого пользователя , Поэтому я подумал, что несколько таблиц для каждого набора будут лучше.

Весь совет приветствуется.

1 Ответ

8 голосов
/ 28 октября 2009

Во-первых, пара замечаний о стиле.

Нет необходимости добавлять префикс полей внешнего ключа к id_.Базовое поле базы данных, которое Django создает для этих FK, в любом случае имеет суффикс _id, поэтому вы получите что-то вроде id_word_id в БД.Ваш код станет намного понятнее, если вы просто назовете поля «слово», «студент» и т. Д.

Кроме того, нет необходимости указывать id автополя в каждой модели.Они создаются автоматически, и вы должны указывать их только в том случае, если вам нужно назвать их как-нибудь еще.Точно так же нет необходимости указывать db_table в вашей мета-версии, поскольку это также выполняется автоматически.

Наконец, нет необходимости вызывать __unicode__ для полей в вашем методе Unicode.Интерполяция строк сделает это автоматически, и, если вы пропустите его, ваш код будет намного легче читать.(Если вы действительно хотите сделать это явно, по крайней мере, используйте форму unicode(self.word).)

В любом случае, к вашему актуальному вопросу.Вы не можете «соединять» наборы запросов как таковые - обычный способ сделать кросс-модельный запрос - это иметь чужой ключ от одной модели к другой.Вы могли бы сделать это:

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

, который под капотом сделает подзапрос, чтобы получить все объекты WordLearned для текущего пользователя и исключить их из списка возвращенных слов.

Однако, особенно учитывая ваши будущие требования к многопользовательскому приложению, я думаю, что вам следует реструктурировать свои таблицы.Вам нужны отношения ManyToMany между Word и Student с таблицей-посредником, которая фиксирует статус Word для конкретного Student.Таким образом, вы можете избавиться от таблиц 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)
...