Pergunta

Usando os modelos integrados do Django, como criar uma junção tripla entre três modelos.

Por exemplo:

  • Usuários, funções e eventos são os modelos.
  • Os usuários têm muitas funções e as funções têm muitos usuários.(Muitos para muitos)
  • Os eventos têm muitos usuários e os usuários muitos eventos.(Muitos para muitos)
  • Mas para qualquer evento, qualquer usuário pode ter apenas uma função.

Como isso pode ser representado no modelo?

Foi útil?

Solução

Zacherates escreve:

Eu modelaria Role como uma classe de associação entre Usuários e Funções (...)

Eu também recomendo esta solução, mas você também pode usar algum açúcar sintático fornecido pelo Django: Relação ManyToMany com campos extras.

Exemplo:

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)

Outras dicas

Eu recomendo apenas criar um modelo totalmente separado para isso.

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

Isso permite que você faça todas as coisas usuais do modelo, como

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

A única coisa que resta é impor sua restrição de uma função por usuário por evento.Você pode fazer isso na classe Assignment substituindo o método save (http://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methods) ou usando sinais (http://docs.djangoproject.com/en/dev/topics/signals/)

Eu modelaria Role como uma classe de associação entre Usuários e Funções, portanto,

class User(models.Model):
     ...

class Event(models.Model):
     ...

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

E aplique uma função por usuário por evento em um gerenciador ou em restrições SQL.

Ao tentar encontrar uma junção de três tabelas mais rápida para meus próprios modelos do Django, me deparei com esta questão.Por padrão, o Django 1.1 usa INNER JOINs que podem ser lentos no InnoDB.Para uma consulta como:

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

isso pode criar o seguinte 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"

Os INNER JOINs podem ser muito lentos em comparação com LEFT JOINs.Uma consulta ainda mais rápida pode ser encontrada na resposta do gimg1: Consulta MySQL para juntar três tabelas

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"

No entanto, você precisará usar uma consulta SQL personalizada: https://docs.djangoproject.com/en/dev/topics/db/sql/

Neste caso, seria algo 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()]
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top