Как создать таблицу с тройным соединением с помощью Django

StackOverflow https://stackoverflow.com/questions/72945

Вопрос

Используя встроенные модели Django, как можно было бы создать тройное соединение между тремя моделями?

Например:

  • Пользователи, Роли и События - это модели.
  • У пользователей много Ролей, а в Ролях много Пользователей.(Многотомный)
  • У событий много пользователей, а у Пользователей много Событий.(Многотомный)
  • Но для любого данного События у любого Пользователя может быть только одна Роль.

Как это может быть представлено в модели?

Это было полезно?

Решение

захераты пишет:

Я бы смоделировал Role как класс ассоциации между пользователями и Ролями (...)

Я бы также рекомендовал это решение, но вы также можете использовать некоторый синтаксический сахар, предоставляемый Django: Связь ManyToMany с дополнительными полями.

Пример:

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)

Другие советы

Я бы рекомендовал просто создать для этого совершенно отдельную модель.

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

Это позволяет вам выполнять все обычные действия с моделью, такие как

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

Единственное, что осталось, - это ввести ограничение по одной роли для каждого пользователя и для каждого события.Вы можете сделать это в классе присваивания, либо переопределив метод save (http://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methods) или используя сигналы (http://docs.djangoproject.com/en/dev/topics/signals/)

Я бы смоделировал Role как класс ассоциации между Пользователями и Ролями, таким образом,

class User(models.Model):
     ...

class Event(models.Model):
     ...

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

И примените одну роль для каждого пользователя в каждом событии либо в менеджере, либо в ограничениях SQL.

Пытаясь найти более быстрое объединение трех таблиц для моих собственных моделей Django, я наткнулся на этот вопрос.По умолчанию Django 1.1 использует ВНУТРЕННИЕ соединения, которые могут быть медленными в InnoDB.Для запроса типа:

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

это может привести к созданию следующего 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"

ВНУТРЕННИЕ соединения могут быть очень медленными по сравнению с ЛЕВЫМИ соединениями.Еще более быстрый запрос можно найти в разделе ответа gimg1: Запрос Mysql для объединения трех таблиц

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"

Однако вам нужно будет использовать пользовательский SQL-запрос: https://docs.djangoproject.com/en/dev/topics/db/sql/

В данном случае это выглядело бы примерно так:

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()]
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top