Domanda

Sto realizzando una piccola app per quiz sul vocabolario e il modello base per una parola è questo:

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'

Il modello di parole su cui mi sto attualmente interrogando è questo:

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'

Dove " livello " indica quanto bene l'ho imparato. L'insieme di parole che ho già imparato ha questo modello:

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'

Quando un queryset su WordToWorkOn ritorna con troppi risultati (perché sono stati appresi abbastanza bene da essere spostati in WordLearned ed eliminati da WordToWorkOn), voglio trovare una parola da aggiungere ad esso. La parte che non conosco un buon modo per fare è limitarla a Words che non sono già in WordLearned.

Quindi, in generale, penso di voler fare un .exclude () di qualche tipo su un queryset di Words, ma deve escluderlo in base all'appartenenza alla tabella WordLearned. C'è un buon modo per farlo? Trovo molti riferimenti all'unione di queryset, ma non ne ho trovato uno buono su come farlo (probabilmente non conosco il termine giusto da cercare).

Non voglio semplicemente usare un flag su ogni Word per indicare appreso, lavoro o non appreso, perché alla fine questa sarà un'app multiutente e non vorrei avere flag per ogni utente . Quindi, ho pensato che più tavoli per ogni set sarebbero stati migliori.

Tutti i consigli sono apprezzati.

È stato utile?

Soluzione

Innanzitutto, un paio di note sullo stile.

Non è necessario aggiungere il prefisso ai campi chiave esterna con id_ . Il campo del database sottostante che Django crea per quegli FK è comunque suffisso con _id , quindi otterrai qualcosa come id_word_id nel db. Renderà il tuo codice molto più chiaro se chiami semplicemente i campi "parola", "studente", ecc.

Inoltre, non è necessario specificare gli autofield id in ciascun modello. Vengono creati automaticamente e dovresti specificarli solo se devi chiamarli qualcos'altro. Allo stesso modo, non è necessario specificare db_table nella tua Meta, poiché anche questo viene fatto automaticamente.

Infine, non è necessario chiamare __unicode__ sui campi nel metodo unicode. L'interpolazione delle stringhe lo farà automaticamente e, ancora una volta, lasciarla fuori renderà il tuo codice molto più facile da leggere. (Se vuoi davvero farlo esplicitamente, usa almeno il modulo unicode (self.word) .)

Comunque, alla tua vera domanda. Non è possibile "unire" i queryset in quanto tali - il modo normale di eseguire una query tra modelli è quello di avere una chiave esterna da un modello all'altro. potresti fare questo:

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

che sotto il cofano farà una sottoquery per ottenere tutti gli oggetti WordLearned per l'utente corrente ed escluderli dall'elenco di parole restituite.

Tuttavia, e soprattutto tenendo presente il tuo futuro requisito per un'app multiutente, penso che dovresti ristrutturare i tuoi tavoli. Quello che vuoi è una relazione ManyToMany tra Word e Student, con una tabella intermedia che catturi lo stato di una Word per un particolare Studente. In questo modo puoi sbarazzarti delle tabelle WordToWorkOn e WordLearned, che sono sostanzialmente duplicati.

Qualcosa del tipo:

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

Ora puoi ottenere tutte le parole da imparare per un determinato studente:

words_to_learn = Word.objects.filter(studentword__student=student, studentword__learned=False)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top