2009-10-28 9 views
0

Je fais une petite application quiz de vocabulaire, et le modèle de base pour un mot est ceci:django QuerySet entrées à l'exception de deuxième modèle

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' 

Le modèle des mots que je suis en train de me questionnant sur est cela:

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' 

Où "niveau" indique à quel point je l'ai appris. L'ensemble des mots que je l'ai déjà appris a ce modèle:

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' 

Lorsqu'un queryset sur WordToWorkOn revient avec des résultats trop peu (parce qu'ils ont été appris assez bien pour se déplacer dans WordLearned et supprimé de WordToWorkOn), Je veux trouver un mot à ajouter. La partie que je ne connais pas un bon moyen est de la limiter aux mots qui ne sont pas déjà dans WordLearned. Donc, de manière générale, je pense que je veux faire un .exclude() de quelque sorte sur un jeu de mots, mais il doit être exclu en fonction de l'appartenance à la table WordLearned. Y at-il un bon moyen de le faire? Je trouve beaucoup de références à se joindre à des jeux de requêtes, mais je n'ai pas trouvé de bonne façon de le faire (je ne connais probablement pas le bon terme à rechercher). Je ne veux pas simplement utiliser un drapeau sur chaque mot pour indiquer appris, travailler dessus, ou pas appris, parce que finalement ce sera une application multi-utilisateur et je ne voudrais pas avoir de drapeaux pour chaque utilisateur. Par conséquent, je pensais que plusieurs tables pour chaque ensemble serait mieux.

Tous les conseils sont appréciés.

Répondre

7

Tout d'abord, quelques notes sur le style.

Il n'est pas nécessaire de préfixer les champs de clé étrangère avec id_. Le champ de base de données sous-jacent que Django crée pour ces FKs est suffixé avec _id de toute façon, donc vous obtiendrez quelque chose comme id_word_id dans la base de données. Cela rendra votre code beaucoup plus clair si vous appelez les champs 'mot', 'étudiant', etc.

De même, il n'est pas nécessaire de spécifier les champs autofocus id dans chaque modèle. Ils sont créés automatiquement et vous ne devez les spécifier que si vous avez besoin de les appeler autrement. De même, pas besoin de spécifier db_table dans votre Meta, car cela est également fait automatiquement.

Enfin, pas besoin d'appeler __unicode__ sur les champs de votre méthode Unicode. L'interpolation de chaîne le fera automatiquement, et à nouveau en l'excluant, votre code sera beaucoup plus facile à lire. (Si vous voulez vraiment le faire explicitement, utilisez au moins le formulaire unicode(self.word).)

De toute façon, à votre question actuelle. Vous ne pouvez pas "joindre" des jeux de requête en tant que tels - la manière normale de faire une requête de modèle croisé est d'avoir une clé étrangère d'un modèle à l'autre. Vous pouvez faire:

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

qui, sous le capot fera une sous-requête pour obtenir tous les objets WordLearned pour l'utilisateur actuel et les exclure de la liste des mots retournés. Cependant, et surtout compte tenu de vos besoins futurs pour une application multi-utilisateur, je pense que vous devriez restructurer vos tables. Ce que vous voulez, c'est une relation ManyToMany entre Word et Student, avec une table intermédiaire capturant le statut d'un mot pour un étudiant particulier.De cette façon, vous pouvez vous débarrasser des tables WordToWorkOn et WordLearned, qui sont essentiellement des doublons.

Quelque chose comme:

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

Maintenant, vous pouvez obtenir tous les mots à apprendre pour un élève en particulier:

words_to_learn = Word.objects.filter(studentword__student=student, studentword__learned=False) 
+0

Wow, trois leçons dans un. Je vais réorganiser ma structure de données en conséquence et revenir à vérifier cette réponse comme correcte une fois que je l'ai effectivement mis en œuvre (pas que j'en doute vraiment, juste par principe). Merci pour votre aide. – rossdavidh

+0

Belle solution, mais comment vous ne pouvez pas prendre des objets Word, ce qui ne sont pas dans la table StudentWord? – inoks

+0

Voici également un bug dans la fonction d'exclusion, il n'est donc pas possible de l'utiliser pour filtrer par 2 champs et plus. https://code.djangoproject.com/ticket/14645 – inoks