2008-09-16 11 views
9

Mit Djangos eingebauten Modellen, wie würde man einen Triple-Join zwischen drei Modellen erstellen.So erstellen Sie einen Triple-Join-Tisch mit Django

Zum Beispiel:

  • Benutzer, Rollen und Events sind die Modelle.
  • Benutzer haben viele Rollen und Rollen viele Benutzer. (ManyToMany)
  • Ereignisse haben viele Benutzer und Benutzer viele Ereignisse. (ManyToMany)
  • Aber für jedes gegebene Ereignis kann jeder Benutzer nur eine Rolle haben.

Wie kann dies im Modell dargestellt werden?

Antwort

8

zacherates schreibt:

I Rolle als Assoziationsklasse zwischen Benutzer und Rollen (...)

ich auch reccomed würde diese Lösung, aber man kann modellieren würde verwende auch etwas syntaktischen Zucker von Django: ManyToMany relation with extra fields.

Beispiel:

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

Dies ist ziemlich neu in Django (seit 1.0). – zgoda

3

Ich würde empfehlen, nur ein völlig separates Modell dafür zu erstellen.

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

Auf diese Weise können Sie das Zeug üblichen Modell alles tun, wie

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

Das einzige, was links ist Ihr One-Rolle-per-user-per-Ereignis Einschränkung zu erzwingen. Sie können entweder in der Zuordnung Klasse tun dies die Methode speichern überschreiben (http://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methods) oder unter Verwendung von Signalen (http://docs.djangoproject.com/en/dev/topics/signals/)

0

I Rolle als Assoziationsklasse zwischen Benutzer und Rollen modellieren würde somit

class User(models.Model): 
    ... 

class Event(models.Model): 
    ... 

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

Und erzwingen Sie die eine Rolle pro Benutzer pro Ereignis in einem Manager oder SQL-Einschränkungen.

0

Bei dem Versuch, eine schnellere Drei-Tabelle für meine eigenen Django-Modelle kommen zu finden, stieß ich auf dieser Frage. Standardmäßig verwendet Django 1.1 INNER JOINs, die bei InnoDB langsam sein können. Für eine Abfrage wie:

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" 

Die INNER JOIN kann im Vergleich sehr langsam mit LEFT JOIN:

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

Dies könnte die folgende SQL erstellen.Eine noch schnellere Abfrage kann unter gimg1 Antwort zu finden: 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" 

Allerdings müssen Sie eine benutzerdefinierte SQL-Abfrage verwenden: https://docs.djangoproject.com/en/dev/topics/db/sql/

In diesem Fall wäre es so etwas wie folgt aussehen:

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