Pregunta

Estoy haciendo una pequeña aplicación de prueba de vocabulario, y el modelo básico para una palabra es esta:

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'

El modelo de palabras en el que actualmente me estoy cuestionando es el siguiente:

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'

Donde " nivel " indica qué tan bien lo he aprendido. El conjunto de palabras que ya aprendí tiene este modelo:

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'

Cuando un conjunto de consultas en WordToWorkOn regresa con muy pocos resultados (porque se han aprendido lo suficientemente bien como para pasar a WordLearned y eliminarlo de WordToWorkOn), quiero encontrar una palabra para agregarle. La parte que no conozco es una buena forma de limitarlo a palabras que aún no están en WordLearned.

Entonces, en términos generales, creo que quiero hacer un .exclude () de algún tipo en un conjunto de consultas de palabras, pero debe excluirse en función de la membresía en la tabla WordLearned. ¿Existe una forma correcta de hacer esto? Encuentro muchas referencias para unir conjuntos de consultas, pero no pude encontrar una buena sobre cómo hacerlo (probablemente no conozco el término correcto para buscar).

No quiero usar solo un indicador en cada palabra para indicar que aprendí, trabajé o no aprendí, porque eventualmente será una aplicación multiusuario y no quisiera tener indicadores para cada usuario . Por lo tanto, pensé que varias tablas para cada conjunto serían mejores.

Todos los consejos son apreciados.

¿Fue útil?

Solución

En primer lugar, un par de notas sobre el estilo.

No es necesario prefijar los campos de clave externa con id_ . El campo de base de datos subyacente que Django crea para esos FK tiene el sufijo _id de todos modos, por lo que obtendrá algo como id_word_id en la base de datos. Hará que su código sea mucho más claro si solo llama a los campos 'palabra', 'estudiante', etc.

Además, no es necesario especificar los campos automáticos id en cada modelo. Se crean automáticamente, y solo debe especificarlos si necesita llamarlos de otra manera. Del mismo modo, no es necesario especificar db_table en su Meta, ya que esto también se hace automáticamente.

Finalmente, no es necesario llamar a __unicode__ en los campos de su método unicode. La interpolación de cadenas lo hará automáticamente y, de nuevo, dejarlo fuera hará que su código sea mucho más fácil de leer. (Si realmente desea hacerlo explícitamente, al menos use el formulario unicode (self.word) ).

De todos modos, a su pregunta real. No puede 'unirse' a conjuntos de consultas como tales: la forma normal de hacer una consulta entre modelos es tener una clave foránea de un modelo a otro. Usted podría hacer esto:

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

que debajo del capó hará una subconsulta para obtener todos los objetos aprendidos de Word para el usuario actual y excluirlos de la lista de palabras devueltas.

Sin embargo, y especialmente teniendo en cuenta sus requisitos futuros para una aplicación multiusuario, creo que debería reestructurar sus tablas. Lo que desea es una relación ManyToMany entre Word y Student, con una tabla intermedia que capture el estado de Word para un estudiante en particular. De esa manera, puede deshacerse de las tablas WordToWorkOn y WordLearned, que son básicamente duplicados.

Algo así como:

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

Ahora puede obtener todas las palabras para aprender de un alumno en particular:

words_to_learn = Word.objects.filter(studentword__student=student, studentword__learned=False)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top