2008-09-16 31 views
9

En utilisant les modèles intégrés de Django, comment créer une triple jointure entre trois modèles.Comment créer une table à triple jointure avec Django

Par exemple:

  • utilisateurs, les rôles et les événements sont les modèles.
  • Les utilisateurs ont de nombreux rôles et de nombreux utilisateurs. (ManyToMany)
  • Les événements ont de nombreux utilisateurs et utilisateurs de nombreux événements. (ManyToMany)
  • Mais pour un événement donné, n'importe quel utilisateur peut avoir un seul rôle.

Comment cela peut-il être représenté dans le modèle?

Répondre

8

zacherates écrit:

Je modèle de rôle en tant que classe d'association entre les utilisateurs et les rôles (...)

Je voudrais aussi reccomed cette solution, mais vous pouvez utiliser également du sucre syntaxique fourni par Django: ManyToMany relation with extra fields.

Exemple:

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) 
+0

Ceci est assez nouveau dans Django (depuis 1.0). – zgoda

3

Je vous recommande de créer un modèle entièrement séparé pour cela.

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

Cela vous permet de faire tous les trucs habituels de modèle, comme

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

La seule chose qui reste est de faire respecter votre restriction un rôle par utilisateur-par-événement. Vous pouvez le faire dans la classe d'affectation soit redéfinissant la méthode save (http://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methods) ou en utilisant des signaux (http://docs.djangoproject.com/en/dev/topics/signals/)

0

Je modélise rôle en tant que classe d'association entre utilisateurs et rôles, donc,

class User(models.Model): 
    ... 

class Event(models.Model): 
    ... 

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

Et appliquer un rôle par utilisateur par événement dans un gestionnaire ou des contraintes SQL.

0

Tout en essayant de trouver un pour mes propres modèles Django trois tables plus rapide se joindre, je suis tombé sur cette question. Par défaut, Django 1.1 utilise INNER JOINs qui peut être lent sur InnoDB. Pour une requête comme:

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

cela pourrait créer l'instruction SQL suivante:

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" 

Le INNER JOIN peut être très lent par rapport à GAUCHE JOIN.Une requête encore plus rapide se trouve sous la réponse de gimg1: Mysql query to join three tables

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" 

Cependant, vous aurez besoin d'utiliser une requête SQL personnalisée: https://docs.djangoproject.com/en/dev/topics/db/sql/

Dans ce cas, il ressemblerait à quelque chose comme:

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