Django の組み込みモデルを使用して、3 つのモデル間のトリプル結合を作成するにはどうすればよいでしょうか。
例えば:
- ユーザー、ロール、およびイベントがモデルです。
- ユーザーには多くの役割があり、役割には多くのユーザーがあります。(多対多)
- イベントには多くのユーザーがあり、ユーザーには多くのイベントがあります。(多対多)
- ただし、特定のイベントでは、ユーザーは 1 つのロールしか持つことができません。
これはモデルでどのように表現できますか?
Django の組み込みモデルを使用して、3 つのモデル間のトリプル結合を作成するにはどうすればよいでしょうか。
例えば:
これはモデルでどのように表現できますか?
zacheratesは次のように書いています。
ユーザーとロールの間の関連クラスとしてロールをモデル化します (...)
私もこの解決策をお勧めしますが、Django が提供する構文糖衣を利用することもできます: ManyToMany relationship with extra fields。
例:
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")
残っている唯一のことは、イベントごとにユーザーごとに 1 つのロールという制限を適用することです。これは、save メソッドをオーバーライドするか ( http://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methods )、またはシグナル ( http: //docs.djangoproject.com/en/dev/topics/signals/ )
私自身の Django モデルでより高速な 3 つのテーブルの結合を見つけようとしているときに、この質問に出くわしました。デフォルトでは、Django 1.1 は InnoDB で遅くなる可能性がある INNER JOIN を使用します。次のようなクエリの場合:
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"
INNER JOIN は、LEFT JOIN に比べて非常に遅くなる可能性があります。さらに高速なクエリは、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"
ただし、カスタム 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()]
ユーザーとロールの間の関連クラスとしてロールをモデル化します。したがって、
class User(models.Model):
...
class Event(models.Model):
...
class Role(models.Model):
user = models.ForeignKey(User)
event = models.ForeignKey(Event)
また、マネージャーまたは SQL 制約のいずれかで、イベントごとにユーザーごとに 1 つのロールを適用します。