набор запросов django, исключая записи во второй модели

StackOverflow https://stackoverflow.com/questions/1637897

Вопрос

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

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

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

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

Это было полезно?

Решение

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

Нет необходимости добавлять к полям внешнего ключа префикс 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)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top