Pregunta

Usando los modelos integrados de Django, ¿cómo se crearía una unión triple entre tres modelos?

Por ejemplo:

  • Los usuarios, roles y eventos son los modelos.
  • Los usuarios tienen muchos roles y los roles muchos usuarios.(Muchos a muchos)
  • Los eventos tienen muchos usuarios y los usuarios muchos eventos.(Muchos a muchos)
  • Pero para cualquier Evento determinado, cualquier Usuario puede tener un solo Rol.

¿Cómo se puede representar esto en el modelo?

¿Fue útil?

Solución

zacherates escribe:

Modelaría Role como una clase de asociación entre Usuarios y Roles (...)

También recomendé esta solución, pero también puedes utilizar algo de azúcar sintáctico proporcionado por Django: Relación ManyToMany con campos adicionales.

Ejemplo:

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

class Event(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(User, through='Role')

    def __unicode__(self):
        return self.name

class Role(models.Model):
    person = models.ForeignKey(User)
    group = models.ForeignKey(Event)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

Otros consejos

Recomendaría simplemente crear un modelo completamente separado para esto.

class Assignment(Model):
    user = ForeignKey(User)
    role = ForeignKey(Role)
    event = ForeignKey(Event)

Esto le permite hacer todas las cosas habituales del modelo, como

user.assignment_set.filter(role__name="Chaperon")
role.assignment_set.filter(event__name="Silly Walkathon")

Lo único que queda es aplicar la restricción de un rol por usuario por evento.Puede hacer esto en la clase Assignment anulando el método de guardar (http://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefinido-model-methods) o usando señales (http://docs.djangoproject.com/en/dev/topics/signals/)

Modelaría Role como una clase de asociación entre Usuarios y Roles, por lo tanto,

class User(models.Model):
     ...

class Event(models.Model):
     ...

class Role(models.Model):
     user = models.ForeignKey(User)
     event = models.ForeignKey(Event)

Y aplique una función por usuario por evento, ya sea en un administrador o en restricciones SQL.

Mientras intentaba encontrar una unión de tres tablas más rápida para mis propios modelos de Django, me encontré con esta pregunta.De forma predeterminada, Django 1.1 usa INNER JOIN que pueden ser lentos en InnoDB.Para una consulta como:

def event_users(event_name):
    return User.objects.filter(roles__events__name=event_name)

esto podría crear el siguiente SQL:

SELECT `user`.`id`, `user`.`name` FROM `user` INNER JOIN `roles` ON (`user`.`id` = `roles`.`user_id`) INNER JOIN `event` ON (`roles`.`event_id` = `event`.`id`) WHERE `event`.`name` = "event_name"

Las INNER JOIN pueden ser muy lentas en comparación con las LEFT JOIN.Se puede encontrar una consulta aún más rápida en la respuesta de gimg1: Consulta mysql para unir tres tablas

SELECT `user`.`id`, `user`.`name` FROM `user`, `roles`, `event` WHERE `user`.`id` = `roles`.`user_id` AND `roles`.`event_id` = `event`.`id` AND `event`.`name` = "event_name"

Sin embargo, necesitará utilizar una consulta SQL personalizada: https://docs.djangoproject.com/en/dev/topics/db/sql/

En este caso, quedaría algo así:

from django.db import connection
def event_users(event_name):
    cursor = connection.cursor()
    cursor.execute('select U.name from user U, roles R, event E' \
                   ' where U.id=R.user_id and R.event_id=E.id and E.name="%s"' % event_name)
    return [row[0] for row in cursor.fetchall()]
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top