Question

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

Par exemple:

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

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

Était-ce utile?

La solution

zacherates écrit:

  

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

Je vous avais aussi recommandé cette solution, mais vous pouvez également utiliser un sucre syntaxique fourni par Django: Relation ManyToMany avec des champs supplémentaires .

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)

Autres conseils

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

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

Ceci vous permet de faire tout ce que vous faites dans les modèles, comme

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

Il ne reste plus qu'à appliquer votre restriction d'un rôle par utilisateur par événement. Vous pouvez le faire dans la classe Assignment en 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/ )

Je modéliserais le 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 le rôle par utilisateur par événement dans un gestionnaire ou des contraintes SQL.

En essayant de trouver une jointure à trois tables plus rapide pour mes propres modèles Django, je suis tombé sur cette question. Par défaut, Django 1.1 utilise INNER JOINs, ce qui peut être lent sur InnoDB. Pour une requête du type:

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

cela pourrait créer le code SQL suivant:

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"

Les jointures internes peuvent être très lentes comparées aux jointures à gauche. Une réponse encore plus rapide se trouve sous la réponse de gimg1: Requête Mysql pour joindre trois 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"

Toutefois, vous devrez utiliser une requête SQL personnalisée: https: //docs.djangoproject.com/fr/dev/topics/db/sql/

Dans ce cas, cela 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()]
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top