2012-02-13 9 views
5

Tengo dos modelos de Django unidos entre sí por la relación ManyToMany así:Conseguir el primer artículo en una relación de muchos a muchos en Django

class Person(models.Model): 
    name = models.CharField(max_length=128) 

class Group(models.Model): 
    name = models.CharField(max_length=128) 
    members = models.ManyToManyField(Person) 

que necesito para obtener la persona principal en el grupo de los cuales es la primera persona en el grupo. ¿Cómo puedo obtener la primera persona?

Así es como estoy añadiendo miembros:

grp = Group.objects.create(name="Group 1") 
grp.save() 
prs = Person.objects.create(name="Tom") 
grp.members.add(prs) #This is the main person of the group. 
prs = Person.objects.create(name="Dick") 
grp.members.add(prs) 
prs = Person.objects.create(name="Harry") 
grp.members.add(prs) 

No creo que sea necesario ningún columnas adicionales como el id de la tabla group_members es una secuencia de funcionamiento correcto.

Si trato de buscar el miembro principal del grupo a través de Group.objects.get(id=1).members[0] entonces Django dice que el administrador no es indexable.

Si pruebo esto Group.objects.get(id=1).members.all().order_by('id')[0], obtengo el miembro con la identificación más baja en la tabla Person.

¿Cómo puedo solucionar esto?

Gracias

+0

¿Cómo desea para determinar quién es la "primera" persona en el grupo? ¿CARNÉ DE IDENTIDAD? ¿Alfabéticamente? – Brandon

+0

El que tiene el 'id' más bajo en la tabla' group_members'. Los objetos nunca se eliminan de esta tabla, por lo que el miembro principal siempre será el mismo para un grupo. –

+0

La respuesta de Alex Gaynor es correcta. Debería agregar un campo adicional para ordenar por el grupo, ya que no se garantiza que la columna ID sea secuencial para el grupo. – Brandon

Respuesta

4

Django no presta atención al orden de las llamadas al add. La tabla implícita Django tiene para la relación es algo así como lo siguiente si se trataba de un modelo de Django:

class GroupMembers(models.Model): 
    group = models.ForeignKey(Group) 
    person = models.ForeignKey(Person) 

Obviamente, no hay nada acerca de un "orden" o que debe ser lo primero. Por defecto, probablemente haga lo que usted describe y devuelva el pk más bajo, pero eso es solo porque no tiene nada más que desaprovechar.

Si desea hacer cumplir un pedido, tendrá que utilizar una tabla directa. Básicamente, en lugar de dejar que Django cree ese modelo implícito, tú mismo lo creas. A continuación, vamos a añadir un campo como order para dictar la orden:

class GroupMembers(models.Model): 
    class Meta: 
     ordering = ['order'] 

    group = models.ForeignKey(Group) 
    person = models.ForeignKey(Person) 
    order = models.PositiveIntegerField(default=0) 

Entonces, le dice a Django a utilizar este modelo para la relación, en su lugar:

class GroupMembers(models.Model): 
    group = models.ForeignKey(Group) 
    person = models.ForeignKey(Person, through='GroupMembers') 

Ahora, cuando se agrega a su miembros, ya no puede usar add, porque se necesita información adicional para completar la relación.En su lugar, debe utilizar el modelo a través de:

prs = Person.objects.create(name="Tom") 
GroupMembers.objects.create(person=prs, group=grp, order=1) 
prs = Person.objects.create(name="Dick") 
GroupMembers.objects.create(person=prs, group=grp, order=2) 
prs = Person.objects.create(name="Harry") 
GroupMembers.objects.create(person=prs, group=grp, order=3) 

A continuación, sólo tiene que utilizar:

Group.objects.get(id=1).members.all()[0] 

Como alternativa, puede simplemente añadir un BooleanField para especificar que es el principal usuario:

class GroupMembers(models.Model): 
    group = models.ForeignKey(Group) 
    person = models.ForeignKey(Person) 
    is_main_user = models.BooleanField(default=False) 

A continuación, agregue "Tom" como:

prs = Person.objects.create(name="Tom") 
GroupMembers.objects.create(person=prs, group=grp, is_main_user=True) 

Y, por último, recuperar "Tom" a través de:

Group.objects.get(id=1).members.filter(is_main_user=True)[0] 
3

base de datos relacional no tienen ninguna noción de orden, de forma predeterminada. No hay tal cosa como "primero". Debe agregar un campo de "orden" explícito que guarde el pedido y ordene por él.

1

Usted eran bastante estrecha:

Group.objects.get(id=1).members.all()[0] 

(Pero como dice Alex Gaynor en su respuesta, por un comportamiento predecible necesita un campo adecuado para ordenar)

+0

He echado un vistazo a la tabla que contiene la relación, es decir 'group_persons' (creo), en la base de datos y tenía una columna' id' aparte de 'group_id' y' person_id'. Como esta es una secuencia en ejecución, ¿no puedo obtener el artículo con el 'id' más bajo? El ejemplo que usted dio: ¿no me devolvería el primer objeto en el conjunto, pero el conjunto no estaría ordenado y por lo tanto sería incorrecto? Gracias. –

+0

No necesita los segundos "objetos" (¿eso funciona?) Simplemente 'members.all()'. –

+0

Tiene razón, mi error. Lo he editado –

0

para mantener de forma fiable un registro de quién es la persona "principal" en un grupo, puede utilizar una tabla through para guardar estos metadatos

Cuestiones relacionadas