python - test - Cómo crear una mesa de triple unión con Django
url pk django (4)
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)
E imponer el único rol por usuario por evento en un administrador o restricciones de SQL.
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 Roles a muchos Usuarios. (Muchos a muchos)
- Los eventos tienen muchos usuarios y muchos eventos. (Muchos a muchos)
- Pero para cualquier Evento dado, cualquier Usuario puede tener solo un Rol.
¿Cómo se puede representar esto en el modelo?
Recomiendo 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 hacer cumplir su restricción de una función por usuario por evento. Puede hacer esto en la clase Asignación anulando el método de guardar ( http://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methods ) o usando señales ( http: //docs.djangoproject.com/en/dev/topics/signals/ )
Zacherates escribe:
Modelaría Role como una clase de asociación entre Usuarios y Roles (...)
También recibí esta solución, pero también puedes utilizar un poco de azúcar sintáctica proporcionada 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)
Al tratar de encontrar una combinació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 JOINs que pueden ser lentas 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"
INNER JOINs puede ser muy lento en comparación con LEFT JOINs. Una consulta aún más rápida se puede encontrar 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, deberá usar una consulta SQL personalizada: https://docs.djangoproject.com/en/dev/topics/db/sql/
En este caso, se vería algo así como:
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()]