【Django】自己参照型リレーションについて分かりやすく解説(同じモデルクラスのインスタンスの関連付け)

Djangoにおける自己参照型リレーションの解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは Django における自己参照型リレーションについて解説していきます。

自己参照型リレーションはリレーションの一種になります。このサイトではリレーションについて下記ページで解説を行なっています。

Djangoのリレーションの解説ページアイキャッチ 【Django入門7】リレーションの基本

上記ページでも解説しているように、リレーションを利用することで2つのモデルクラスの間に関連性を持たせることができます。

例えば、上記ページの 掲示板アプリでリレーションを利用してみる では、リレーションを利用してユーザーとコメントとを関連付け、ユーザーをコメントの投稿者として扱う例を示しています。

この関連付けは異なるモデルクラスのインスタンス同士で行うことも多いのですが、同じモデルクラスのインスタンス同士を関連づけることも可能で、そのような関連付けのことを自己参照型リレーションと呼びます。

このような関連付けを行うメリットをすぐには思い付かない方もおられるかもしれませんが、実は自己参照型リレーションを利用するメリットは多く、これを利用することで実現可能となる機能も多いです。例えばコメントの返信やフォロー機能なども、自己参照型リレーションを利用することで簡単に実現できます。

同じモデルクラスのインスタンス同士を関連付けるという点にややこしさを感じるかもしれませんが、図やコードで補足しながらできるだけ分かりやすく解説していきますので、是非このページで自己参照型リレーションについて学んでいっていただければと思います。

自己参照型リレーション

では、自己参照型リレーションについて解説していきます。

前述の通り、一般的なリレーションは「2つのモデルクラスのインスタンスの関連付け」のことになります。それに対し、自己参照型リレーションは「同じモデルクラスのインスタンス同士の関連付け」のことになります。

下記ページでも解説していますが、モデルクラスはデータベースのテーブルであり、モデルクラスのインスタンスはそのテーブルのレコードに該当します。

モデルの解説ページアイキャッチ 【Django入門6】モデルの基本

つまり、自己参照型リレーションとは、同じテーブル内のレコードを関連付けることであると考えることができます。

同じテーブル内のレコード間に関連性を持たせる様子

このページでは、このような関連付けのことを自己参照型リレーションと呼びますが、他にも自己結合などと呼ぶこともあるようです。

自己参照型リレーションの実現方法

続いて、自己参照型リレーションの実現方法について解説していきます。

スポンサーリンク

自分自身のモデルクラスに対して関連性を持たせる

まず、リレーションは、モデルクラスに対してリレーションフィールドを定義することで実現することができます。

Django においては、リレーションフィールドとして下記の3つが用意されており、これらをモデルクラスに定義する、つまり、これらのインスタンスを値とするクラス変数を定義してやることで、2つのモデルクラスに関連性を持たせ、それらのインスタンス同士を関連付けることができるようになります。

  • OneToOneField:1対1のリレーション
  • ForeignKey:多対1のリレーション
  • ManyToManyField:多対多のリレーション

上記の3つのフィールドの違いや詳細に関しては下記ページでまとめていますので、詳しくは下記ページを参照していただければと思います。このページでは、まずは主に ForeignKey を利用する例を用いて自己参照型リレーションの解説を行なっていきます。その後、ManyToManyField における自己参照型リレーションManyToManyField で自己参照型リレーションを実現する場合の特記事項・注意事項について解説していきます。

Djangoのリレーションの解説ページアイキャッチ 【Django入門7】リレーションの基本

さて、リレーションフィールドについての解説に話を戻すと、リレーションフィールドを定義する際には、リレーションフィールドの第1引数にモデルクラスを指定する必要があります。これにより、リレーションフィールドを定義したモデルクラスのインスタンスと、第1引数に指定したモデルクラスのインスタンスとで関連付けを行うことができるようになります。

例えば、下記のようにモデルクラスを定義した場合、Student のインスタンスと Club のインスタンスとで多対1の関連付けが行えるようになります。

リレーションフィールドの定義
from django.db import models

class Club(models.Model):
    name = models.CharField(max_length=32)

class Student(models.Model):
    name = models.CharField(max_length=32)
    club = models.ForeignKey(Club, on_delete=Club)

このように、リレーションフィールドの第1引数に、そのリレーションフィールドを定義するモデルクラスとは異なるモデルクラスを指定すれば、異なるモデルクラスのインスタンスを関連付けることができることになります。

通常のリレーションの説明図

それに対し、リレーションフィールドの第1引数に、そのリレーションフィールドを定義するモデルクラスと同じモデルクラス、すなわち自分自身のモデルクラスを指定すれば、同じモデルクラスのインスタンス同士を関連付けることができることになります。これこそが自己参照型リレーションであり、つまりは自己参照型リレーションを実現するためには、リレーションフィールドの第1引数に自分自身のモデルクラスを指定すればよいことになります。

より具体的には、下記のようにモデルクラスの定義を行えば、自己参照型リレーションを実現することができることになります。

自己参照型リレーション
class モデルクラス名(models.Model):
    フィールド名 = models.ForeignKey('モデルクラス名', 略)

例えば、下記のように  Student を定義すれば、

自己参照型リレーションの例
class Student(models.Model):
    loving = models.ForeignKey('Student', on_delete=models.CASCADE)

Student のインスタンス同士を関連付けることができることになります。

自己参照型リレーションの説明図

この辺りの、実際のインスタンス同士の関連付けは、通常のリレーションの時同様にビューやモデルクラスのメソッドで実施することになります。また、自己参照型リレーションでのインスタンスの関連付けには、通常のリレーションには無い特徴がありますので、その点については、後述の 能動的な関連付けと受動的な関連付け の節で解説を行います。

自分自身のモデルクラスは文字列で指定する

ただし、自分自身のモデルクラスをリレーションフィールドの引数に指定する際には、その自分自身のモデルクラスを必ず文字列として指定する必要があります。この点は自己参照型リレーション実現時のポイントになると思います。

自己参照型リレーションフィールドの第1引数には文字列を指定する必要がある点を説明する図

そのため、先ほど示したモデルクラスの定義例においては、ForeignKey の第1引数を Student ではなく 'Student' とシングルクォーテーションマークで囲う形で記述しています。もちろんダブルクォーテーションマークで囲っても良いです。重要なのは、文字列として指定することになります。

もしくは、リレーションフィールドの第1引数に 'self' を指定した場合も自己参照型リレーションが実現できることになります。ただし、この場合も文字列で指定する必要がある点に注意してください。

自分自身をselfで指定する例
class モデルクラス名(models.Model):
    フィールド名 = models.ForeignKey('self', 略)

下記のようにフィールドの第1引数への指定が文字列ではなく、モデルクラスそのもの(クラスオブジェクト)を指定した場合、マイグレーション実行時等に例外が発生することになるので注意してください。

自己参照型リレーションの設定(NG)
class Student(models.Model):
    loving = models.ForeignKey(Student, on_delete=models.CASCADE)

実際に発生する例外は下記のようなものになります。

name 'モデル名' is not defined

Python インタプリタはモデルクラスの中で定義しているフィールドを全て認識した後にモデルクラス自体を認識するようになっているため、自身の定義の中で自分自身を参照すると、まだ認識していないモデルクラスが使用されているとみなされて例外が発生することになります。

また、以降では、第1引数に '自分自身のモデルクラス名' or 'self' を指定したリレーションフィールドのことを『自己参照型リレーションフィールド』と呼ばせていただきます。

自己参照型リレーションの特徴

自己参照型リレーションの実現方法については理解していただけたと思いますので、次は自己参照型リレーションの特徴について説明していきます。

スポンサーリンク

同一のモデルクラスのインスタンスを関連付け可能

まず、自己参照型リレーションにおける特徴の1つ目として、同じモデルクラス(テーブル)のインスタンス同士を関連付けることができる点が挙げられます。これは、ここまで説明してきた通りですね!

ウェブアプリにおいて、同じモデルクラスのインスタンス同士を関連付ける機会は多いです。

例えば、掲示板アプリのコメントの返信機能は、コメント同士の関連付けによって実現されることになります。関連付けの意味合いを「返信する」と解釈すれば、コメントBコメントA を関連付けることで、コメントBコメントA への返信であることが管理できることになります。

コメントの返信が、コメントとコメントの関連付けによって実現できることを示す図

この場合、同じコメント同士の関連付けになりますので、自己参照型リレーションによってコメントの返信が実現できることになります。例えば下記のように Comment を定義すれば、この Comment を利用するウェブアプリではコメントの返信機能が実現できることになります。

コメントの返信の管理
from django.db import models

class Comment(models.Model):
    text = models.CharField(max_length=256)
    reply = models.ForeignKey('self', on_delete=models.CASCADE)

他にも、SNS 等のフォロー機能に関しても、ユーザーがユーザーをフォローするというユーザー同士の関連付けによって実現可能ですし、親子関係を管理する機能に関しても、人が人の親(もしくは人が人の子)という人同士の関連付けによって実現可能です。

SNSのフォロー機能も自己参照型リレーションによって実現できることを説明する図

こんな感じで、同じモデルクラスのインスタンスを関連付けることで様々な機能が実現可能で、同じモデルクラスのインスタンスの関連付けが必要になる場面は多いです。そして、この同じモデルクラスのインスタンスの関連付けを実現するのが自己参照型リレーションとなります。

追加されるデータ属性は2つ

自己参照型リレーションの特徴の2つ目として、自己参照型リレーションフィールドを定義することによって、そのフィールドが定義されたモデルクラスのインスタンスに2つのデータ属性が追加される点が挙げられます。

MEMO

ManyToManyField を利用する場合、自己参照型リレーションフィールドを定義した場合でも、引数の指定によっては追加されるデータ属性が1つのみとなることがあります

これに関しては、後述の ManyToManyField における自己参照型リレーション で詳細を説明します

下記ページで解説しているように、リレーションフィールドを定義することによって、そのフィールドの定義先のモデルクラスのインスタンスと、そのフィールドの第1引数で指定したモデルクラスのインスタンスのそれぞれにデータ属性が追加されることになります。

Djangoのリレーションの解説ページアイキャッチ 【Django入門7】リレーションの基本

そして、この追加されるデータ属性の名称やデータ属性の管理対象をまとめた表が下図となります。表内の 関連モデルクラス は、リレーションフィールドの第1引数に指定したモデルクラスを示しています。また、追加されるデータ属性の 相手のモデルクラス名 の部分はすべて小文字となりますし、関連モデルクラス に追加されるデータ属性の名称は related_name 引数で変更することも可能です。

リレーションフィールドの定義によって追加されるデータ属性についてまとめた表

例えば下記のように ClubStudent を定義したのであれば、Student にはリレーションフィールドのフィールド名、すなわち club というデータ属性が追加され、さらに Club には、相手のモデルクラス名_set、すなわち student_set というデータ属性が追加されることになります。

リレーションフィールドの定義
from django.db import models

class Club(models.Model):
    name = models.CharField(max_length=32)

class Student(models.Model):
    name = models.CharField(max_length=32)
    club = models.ForeignKey(Club, on_delete=models.CASCADE)

自己参照型リレーションフィールドにおいても、結局追加されるデータ属性は先ほど示した表と同様になります。ですが、自己参照型リレーションフィールドの場合、関連モデルクラス = フィールドの定義先のモデルクラス となりますので、リレーションフィールドを定義したモデルクラスに両方のモデルクラスに対するデータ属性が追加されることになります。

例えば下記のように Student を定義したのであれば、Student にはリレーションフィールドのフィールド名、すなわち loving というデータ属性と、相手のモデルクラス名_set、すなわち student_set というデータ属性が追加されることになります。

リレーションフィールドの定義
from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=32)
    loving = models.ForeignKey('self', on_delete=models.CASCADE)

ということで、一般的なリレーションフィールドを定義する時と同じルールでデータ属性が追加されることになります。ただ、関連モデルクラス = フィールドの定義先のモデルクラス という点が異なるだけです。関連モデルクラス = フィールドの定義先のモデルクラス を考慮して追加されるデータ属性について整理した表が下の図になります。

自己参照型リレーションフィールドの定義によって追加されるデータ属性についてまとめた表

また、上図の表にも記していますが、リレーションフィールドを定義することでインスタンスに追加されるデータ属性は、そのインスタンスに関連付け可能なインスタンスの個数に応じて管理対象となるオブジェクトが異なります。

関連付け可能なインスタンスの個数が であるデータ属性では、相手のモデルクラスの1つのインスタンスを単に管理することになります。また、関連付け可能なインスタンスの個数が であるデータ属性では、相手のモデルクラスのインスタンスの集合を管理することになります。

通常のリレーション同様に、この辺りを意識しながらデータ属性を扱うことが重要となります。

能動的な関連付けと受動的な関連付け

とりあえず、自己参照型リレーションフィールドを定義したモデルクラスに2つのデータ属性が追加されることは理解していただけたのではないかと思います。

問題は、これらの2つのデータ属性をどう使い分けるのか?という点になります。ここがややこしい…。

結論としては、これらの2つのデータ属性は「能動的な関連付け」を行う場合と「受動的な関連付け」を行う場合とで使い分けることになります。

この点について、下記の Student を例にして解説してきたいと思います。

リレーションフィールドの定義
from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=32)
    loving = models.ForeignKey('self', on_delete=models.CASCADE, null=True)

使用するデータ属性による関連付けの違い

まず、上記のように Student を定義すれば、Student のインスタンスには loving というデータ属性と student_set というデータ属性の2つが追加されることになります。

また、loving のデータ属性で管理するのは1つのインスタンスなので、同じモデルクラス(Student)の1つのインスタンスと関連付けを行うことができることになります。それに対し、student_set のデータ属性で管理するのは集合なので、同じモデルクラスの複数のインスタンスとの関連付けを行うことができることになります。

Studentのインスタンスに追加される2つのデータ属性の管理対象のオブジェクトを示す図

ここで、実際に studentAstudentB の2つの Student のインスタンスで、これらの関連付けの意味合いを確認していきましょう。

まず、studentA から loving で studentB を関連付けることを考えていきましょう!この studentA からの studentB の関連付けは、下記のような処理によって実現可能です。

lovingでの関連付け
studentA.loving = studentB
studentA.save()

ここで、loving での関連付けを「好き」という意味合いで捉えるとすると、この関連付けによって「studentA は studentB が好き」という関係性が生まれることになります。これは、studentA で考えると能動的な関連付けであると考えられます。つまり、この時の loving は能動的な関連付けを管理するためのデータ属性となります。

データ属性lovingによって能動的な関連付けを実施する様子

と、同時に、「studentBstudentA から好かれている」という関係性も生まれることになりますね。これは、studentB で考えると受動的な関連付けであると考えられます。この例だけでなく、一方のインスタンスからの能動的な関連付けが行われると、その裏で、必ず他方のインスタンスでの受動的な関連付けが発生することになります。これは、現実の世界でも同様です。

そして、このような受動的な関連付けは、追加される2つのデータ属性の内、能動的な関連付けを管理するためのデータ属性と “異なる方のデータ属性” で管理されることになります。つまり、今回の場合は student_set が受動的な関連付けを管理するためのデータ属性ということになります。この例においては、student_set での関連付けは「好かれている」という意味合いになりますね!そして、前述の通り、一方のインスタンスからの能動的な関連付けが行われると、その裏で、必ず他方のインスタンスでの受動的な関連付けが発生することになりますので、studentA.loving によって studentB が能動的に関連付けられると、自動的に、studentB.student_set によって studentA が関連付けられることにもなります。

自動的にstudent_setでの関連付けが実施される様子

ここまで説明してきたように、自己参照型リレーションフィールドの定義によって、そのフィールドの定義先のモデルクラスのインスタンスには2つのデータ属性が追加されることになり、一方のデータ属性は能動的な関連付けを行い、他方のデータ属性は受動的な関連付けを行うものとなります。そして、一方のインスタンスから能動的な関連付けを行えば、他方のインスタンスでも受動的な関連付けが行われることになります。この辺りが、ここまでの説明のポイントになります。

一方のインスタンスから能動的な関連付けが行わレルことにより、他方のインスタンスでの受動的な関連付けが自動的に実施されることを示す図

ただ、通常のリレーションフィールドの追加によって追加されるデータ属性に関しても、追加先が異なるモデルクラスのインスタンスにはなるものの、能動的な関連付けと受動的な関連付けの2つが追加されるという点は同様になります。そして、一方のインスタンスから能動的な関連付けを行えば、他方のインスタンスでも受動的な関連付けが行われるという点も同様です。

通常のリレーションでも能動的な関連付けと受動的な関連付けが実施されることを示す図

ですが、通常のリレーションフィールドを定義した場合は、一方のインスタンスからは能動的な関連付け or 受動的な関連付けの片方しか実施できません。それに対し、自己参照型リレーションフィールドを定義した場合、そのフィールドの定義先のモデルクラスのインスタンスには2つのデータ属性の両方が追加されるため、各インスタンスで能動的な関連付けと受動的な関連付けの両方が実施できることになります。ここは自己参照型リレーションフィールドの特徴であると言えると思います。

自己参照型リレーションの場合、1つのインスタンスから能動的な関連付けも受動的な関連付けも実施可能であることを示す図

ということで、次は、studentA から student_set で studentB を関連付けることを考えていきましょう!この studentA からの studentB の関連付けは、下記のような処理によって実現可能です。

student_setでの関連付け
studentA.student_set.add(studentB)

この関連付けは、先ほどと同様に studentA からの studentB の関連付けとなりますが、意味合いは大きく異なります。先ほどの説明の通り、loving での関連付けは能動的な「好き」という意味合いになりますが、student_set での関連付けは受動的な「好かれている」という意味合いになります。なので、上記の関連付けによって「studentA が studentB に好かれている」という関係性が生まれることになります。そして、これは studentA で考えると受動的な関連付けであると考えられます。

データ属性student_setによって受動的な関連付けを実施する様子

で、これによって「studentBstudentA が好き」という関係性も生まれるので、studentB.lovingstudentA を参照することになります。

自動的にlovingでの関連付けが実施される様子

このように、自己参照型リレーションの場合、1つのインスタンスから能動的な関連付けも受動的な関連付けも実施することが可能です。そして、一方のインスタンスから能動的 or 受動的な関連付けを実施すれば、自動的に他方のインスタンスでの受動的 or 能動的な関連付けも実施され、能動的な関連付けと受動的な関連付けが辻褄が合うように Django フレームワークによって制御されることになります。

また、関連付けられたインスタンスの取得に関しても通常のリレーションと同様の方法で実施することが可能です。ですが、自己参照型リレーションの場合は、1つのインスタンスから能動的な関連付けが行われたインスタンスと、受動的な関連付けが行われたインスタンスの両方を取得することが可能です。

例えば、下記のような処理を実施すれば、studentA から能動的な関連付けが行われている Student のインスタンス、つまり 「studentA が好きな Student のインスタンス」の name を出力することができることになります。

lovingの参照するインスタンスの出力
print(studentA.loving.name)

また、下記のような処理を実施すれば、studentA から受動的な関連付けが行われている Student の全インスタンス、つまり「studentA のことを好きな全 Student のインスタンス」の name を出力することができることになります。

student_setに含まれるインスタンスの出力
for student in studentA.student_set.all()
    print(student.name)

もちろん、上記の処理によって、他のインスタンスから studentA に能動的な関連付けが行われたインスタンスも出力されることになります。

例えば下記のような処理を実行すれば、studentA から受動的な関連付けが行われた studentB だけでなく、studentA と能動的な関連付けを行なった studentCstudentDname も出力されることになります。

student_setに含まれるインスタンスの出力例
studentA.student_set.add(studentB)
studentC.loving = studentA
sutndetD.loving = studentA

for student in studentA.student_set.all()
    print(student.name)

こんな感じで、自己参照型リレーションフィールドの定義によって追加される2つのデータ属性は「能動的な関連付け管理するデータ属性」 or 「受動的な関連付けを管理するデータ属性」となり、それぞれで役割が異なります。

そして、これらのデータ属性を利用することで、特定のインスタンスから能動的な関連付けと受動的な関連付けが行われることが可能です。ただ、基本的には関連付けの実施に関しては能動的な関連付けを実施するように心がけるので良いと思います。それに対し、関連付けられたインスタンスの取得に関しては、能動的な関連付けが行われているインスタンス or 受動的な関連付けが行われているインスタンスのどちらを取得したいのかによって取得先のデータ属性を使い分ける必要があります。

どちらのデータ属性が能動的 or 受動的になる?

次は、追加される2つのデータ属性の内、どちらが能動的で、どちらが受動的となるのか?という点について解説していきます。

これは結局、ウェブアプリ内での2つのデータ属性の扱い方次第になります。

例えば、先ほどと同じく下記の Student の例で、今度は loving を受動的な関連付けを管理するデータ属性として扱うことを考えていきましょう。

リレーションフィールドの定義
from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=32)
    loving = models.ForeignKey('self', on_delete=models.CASCADE, null=True)

この場合、先ほどとは逆に loving での関連付けは「好かれている」という意味合いになりますので、下記を実行した場合、「studentAstudentB に好かれている」という関係性が生まれることになります。

lovingでの関連付け
studentA.loving = studentB
studentA.save()

同様に、下記のように studentA から student_set で studentB を関連付ければ、「studentBstudentA が好き」という関係性が生まれることになります。

student_setでの関連付け
studentA.student_set.add(studentB)

さらに、下記のような処理を実施すれば、「studentA のことを好きな Student のインスタンス」の name を出力することができることになりますし、

lovingの参照するインスタンスの出力
print(student.loving)

下記のような処理を実施すれば、「studentA が好きな全 Student のインスタンス」の name を出力することができることになります。

student_setに含まれるインスタンスの出力
for student in studentA.student_set.all()
    print(student.name)

ここまで示してきた処理は、loving を能動的な関連付けを管理するデータ属性として説明してきたときに紹介したものと全く同じものになります。もちろん、loving を受動的な関連付けを管理するデータ属性として扱うようになったことで、各処理の意味合いは異なっていますが、処理内容としては矛盾していません。

このように、2つのデータ属性のどちらを能動的な関連付けを管理するデータ属性として扱ったとしても、別に矛盾なくウェブアプリを開発することは可能です。重要なことは、2つのデータ属性の内、どちらを能動的な関連付けを管理するデータ属性とし、どちらを受動的な関連付けを管理するデータ属性とするのかを事前に開発者自身が決めておき、それに従ってモデル以外(ビューやテンプレートファイルなど)の実装を行うことになります。

関連付け可能なインスタンスの個数とデータ属性の役割

ただ、特に ForeignKey を自己参照型リレーションフィールドとして定義する場合、それぞれのデータ属性での関連付け可能なインスタンスの個数によって、大体どちらを能動的 or 受動的なものとして扱えば良いのかが自然と決まることになると思います。

例えば、ここまで例として利用してきた下記の Student で考えると、loving での関連付け可能なインスタンスの個数は  となり、student_set での関連付け可能なインスタンスの個数は  となります。

リレーションフィールドの定義
from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=32)
    loving = models.ForeignKey('self', on_delete=models.CASCADE, null=True)

したがって、loving での関連付けを「好き」という能動的な関連付け、さらに student_set での関連付けを「好かれている」という受動的な関連付けとすると、各 Student が「好き」になれる人数は のみであり、各 Student が「好かれる」人数は ということになります。

lovingを「好き」という能動的な関連付けとした場合に実現可能なインスタンスの関係を示した図

逆に、loving での関連付けを「好かれる」という受動的な関連付け、さらに student_set での関連付けを「好き」という能動的な関連付けとすると、各 Student が「好き」になれる人数は となり、各 Student が「好かれる」人数は  ということになります。

lovingを「好かれる」という受動的な関連付けとした場合に実現可能なインスタンスの関係を示した図

このように、ForeignKey を自己参照型リレーションフィールドとして定義する場合は、フィールド名 のデータ属性での関連付けが可能なインスタンスの個数は となり、相手のモデルクラス名_set のデータ属性での関連付けが可能なインスタンスの個数は  となるように決まっています。なので、能動的に関連付け可能なインスタンスの個数を  としたい場合は、フィールド名 のデータ属性を能動的な関連付けを管理するデータ属性とすればよいですし、逆に能動的に関連付け可能なインスタンスの個数を  としたい場合は、相手のモデルクラス名_set のデータ属性を能動的な関連付けを管理するデータ属性とすればよいことになります。

このように、ForeignKey を自己参照型リレーションフィールドとして定義するのであれば、関連付け可能なインスタンスの個数から、どちらを能動的 or 受動的な関連性を管理するデータ属性とするかを決めてやれば良いことになります。

また、能動的に関連付け可能なインスタンスの個数も、受動的に関連付け可能なインスタンスの個数も  としたいのであれば、OneToOneField を自己参照型リレーションフィールドとして定義すればよいですし、両方を  としたいのであれば、ManyToManyField を自己参照型リレーションフィールドとして定義すればよいことになります。

で、ここまで挙げてきた例で考えると、おそらく同時に複数の人から好かれることがあり得るという点は皆さん意見が一致するのではないかと思います。ただ、同時に複数の人を好きになることがあるかどうかは意見が分かれるところかもしれないですね…。とりあえず、同時に複数の人を好きになることが無い前提でウェブアプリを開発するのであれば、下記のように Student を定義して loving を能動的な関連付けを管理するデータ属性と考えれば良いと思います。

同時に一人しか好きになれない場合のSutdent
from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=32)
    loving = models.ForeignKey('self', on_delete=models.CASCADE, null=True)

もし、同時に複数の人を好きになることがある前提でウェブアプリを開発するのであれば、下記のようにリレーションフィールドを ManyToManyField で定義するようにしてやれば良いことになります。

同時に複数人を好きになれる場合のSutdent
from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=32)
    loving = models.ManyToManyField('self')

データ属性の名称が超重要

ここまで説明してきたように、2つのデータ属性の役割は「能動的な関連付けを管理するデータ属性」 or 「受動的な関連付けを管理するデータ属性」のどちらかになります。

“どちらかになる” という点が非常にややこしいので、データ属性の役割が明確に分かるようにデータ属性を名付けることが自己参照型リレーションを使いこなすポイントになります。

というか、役割がデータ属性の名称から判断できないとコーディング時に混乱しますので、必ずデータ属性は能動的なものか受動的なものかが判断できるように名付けるようにしましょう。

例えば、先ほどの説明の中で下記の Student の loving を受動的な関連付けを管理するデータ属性として扱う例を示しました。

リレーションフィールドの定義
from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=32)
    loving = models.ForeignKey('self', on_delete=models.CASCADE, null=True)

ハッキリ言って、これは最悪で、受動的なものとして扱うのにデータ属性の名称が能動的な意味合いの loving だと、モデルクラスの定義者以外は意味不明に感じますし、データ属性の名称に反対の意味の言葉を用いるとバグが発生する可能性も上がります。また、追加されるもう1つの方のデータ属性も student_set となり、これが能動的なものとして扱えば良いのか受動的なものとして扱えば良いのかがデータ属性の名称から判断できません。

能動的な関連付け管理するデータ属性であれば「能動的な名前」を、受動的な関連付け管理するデータ属性であれば「受動的な名前」を付けるようにするべきです。

例えば、リレーションフィールドの フィールド名 の名前がつけられるデータ属性を受動的な関連付け(好かれている)を管理するデータ属性とするのであれば、下記のように Student を定義した方がよいでしょう。下記のようにリレーションフィールドに related_name 引数を指定することで 相手のモデルクラス名_set の方のデータ属性の名称も任意に設定できますので、特に自己参照型リレーションフィールドの場合は related_name 引数を積極的に利用することをオススメします。

適切なデータ属性の名称設定
from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=32)
    loved_by = models.ForeignKey('self', on_delete=models.CASCADE, null=True, related_name='loving')

これにより、Student のインスタンスには loved_byloving という名称の2つのデータ属性が追加されることになります。そして、このデータ属性の名称より、loved_by が受動的な関連付けを管理し、loving が能動的な関連付けを管理することが明白になります。

また、例えば下記のような処理を読むだけで「studentA は studentB に好かれている」という関係性が一目瞭然になりますし、

適切なデータ属性での関連付け1
studentA.loved_by = studentB

例えば下記のような処理を読むだけで「studentA は studentB が好き」という関係性が一目瞭然になります。

適切なデータ属性での関連付け2
studentA.loving.add(studentB)

このように、データ属性の名称を適切に設定すれば、その関連付けの意味合いを深く考えなくても理解できるようになり、ソースコードの可読性を飛躍的に向上させることができます。

自己参照型リレーションの場合、2つのデータ属性が追加される点だけでもややこしいのですが、それぞれのデータ属性の意味合いが異なるので余計にややこしくなります。なので、特に自己参照型リレーションの場合は、データ属性の名称から、そのデータ属性の意味合いが理解できるようにデータ属性の名称を設定することが重要となります。

スポンサーリンク

多段階の関連性が簡単に実現できる

自己参照型リレーションの面白いところは、自己参照型リレーションフィールドを定義することで1つのインスタンスに2つのデータ属性が追加され、1つのインスタンスから能動的な関連付けと受動的な関連付けの両方が実施できるという点にあります。

そして、これによって多段階の関連性が簡単に実現することが可能となります。

通常のリレーションフィールドを定義した場合、そのフィールドの定義先のモデルクラスと、そのフィールドの第1引数で指定したモデルクラスの両方にデータ属性が1つずつのみ追加されることになります。そして、一方のモデルクラスに追加されるデータ属性が能動的なインスタンスの関連付けを管理し、他方のモデルクラスに追加されるデータ属性が受動的なインスタンスの関連付けを管理するデータ属性となります。

なので、特定のインスタンスから実施可能なのは能動的な関連付け or 受動的な関連付けの一方のみとなります。

そのため、自己参照型リレーションの時とは異なり、少なくとも一つのリレーションフィールドの定義では多段階の関連付けが実現できません。

例えば、下記のようなモデルクラスを使ってコメントの返信機能を実現することについて考えてみましょう!

通常のリレーションでのコメントの関連付け
from django.db import models

class Comment(models.Model):
    text = models.CharField(max_length=256)

class ReplyComment(models.Model):
    text = models.CharField(max_length=256)
    reply = models.ForeignKey(Comment, related_name='replied_by', on_delete=models.CASCADE)

上記のように Comment と ReplyComment を定義した場合、ReplyComment のインスタンスには reply というデータ属性が、Comment のインスタンスには replied_by というデータ属性が追加されることになり、それぞれのデータ属性からインスタンスの関連付けを実施することが可能となります。

ここで、ReplyComment からの reply での関連付けは「返信する」という能動的な意味合いであると考えて説明していきたいと思います。例えば、reply_commentReplyComment のインスタンス、commentComment のインスタンスと考えれば、下記を実行することで「reply_commentcomment に返信した」という関連性が生まれることになります。

ReplyCommentからの関連付け
reply_comment.reply = comment
reply_comment.save()

と同時に「commentreply_comment から返信された」という関連性も生まれることになり、上記の処理によって comment.replied_by の集合に reply_comment が追加されることになります。

ReplyCommentのインスタンスとCommentのインスタンスの関連付けの説明図

そして、このような関連付けにより、コメント(Comment のインスタンス)への返信機能を実現することができることになります。

ですが、一点問題があります。それは、返信コメント(ReplyComment)への返信ができないという点になります。

ReplyComment のインスタンスは能動的な関連付けを行う reply を持っているので、前述の通り「返信する」という能動的な関連付けを Comment のインスタンスに対して行うことは可能です。ですが、ReplyComment のインスタンスは受動的な関連付けを行う replied_by は持っていないので「返信される」という受動的な関連付けを行うことはできないのです。

ReplyCommentのインスタンスが返信することはできても返信されることができないことを示す図

つまり、通常のリレーションフィールドを1つ定義しただけでは、1つのインスタンスから能動的な関連付け or 受動的な関連付けの一方向の関連付けしか行うことができません。1つのインスタンスが両方の関連付けを行うことができないため、例えば下の図のような「返信コメントに返信する」や「返信コメントの返信コメントに返信する」ような多段階な関連付けは実現できないことになります。

多段階な関連付けの説明図

それに対し、自己参照型リレーションの場合、1つのインスタンスが能動的な関連付けと受動的な関連付けの両方を実施することが可能なので、上の図のような多段階の関連付けも簡単に実現することができます。

ということで、次は下記の Comment を使ってコメントの返信機能を実現することについて考えてみましょう!

自己参照型リレーションでのコメントの関連付け
from django.db import models

class Comment(models.Model):
    text = models.CharField(max_length=256)
    reply = models.ForeignKey('Comment', related_name='replied_by', on_delete=models.CASCADE)

このように Comment を定義すれば、Comment のインスタンスには replyreplied_by の2つのデータ属性が追加されることになります。先ほどと同様に、reply での関連付けは「返信する」という能動的な意味合いであると、この Comment のインスタンスは、reply によって能動的な関連付けも実施可能であり、さらに replied_by によって受動的な関連付けも実施可能となります。

Commentのインスタンスが返信することも返信されることも可能であることを示す図

例えば、commentAcommentBcommentC とを Comment のインスタンスとすれば、まず下記を実行することで「commentB は commentA に返信した」という関連性が生まれることになります。

commentBからcommentAへの関連付け
commentB.reply = commentA
commentB.save()

つまり、commentBcommentA への返信コメントということになりますね!

さらに、下記の2つの処理のいずれかを実行すれば、次は「commentC は commentB に返信した」という関連性が生まれることになります。下記の2つの処理は、関連付けの仕方は異なるものの、関連付けの結果は同じとなります。

commentBからcommentCへの関連付け
commentB.replied_by.add(commentC)
commentCからcommentBへの関連付け
commentC.reply = commentB
commentC.save()

これにより、commentCcommentB への返信コメントということになります。そして、commentBcommentA への返信コメントですので、返信コメントへの返信が実現できているということになります。

commentAとcommentBとcommentCの関連性を示す図

ここでのポイントは commentB の関連付けで、この commentB からは commentA との能動的な関連付けと commentC との受動的な関連付けの両方が行われているという点になります。このように、一方向に対して多段階の関連付けを行おうとすると、各インスタンスには能動的な関連付けと受動的な関連付けの両方が必要となります。

そして、自己参照型リレーションの場合は、自己参照型リレーションフィールドを定義するだけで各インスタンスがそれを満たすことになるので、一方向に対する多段階の関連付けも簡単に実現することができることになります。

こういった多段階の関連付けが必要になる場面も多いです。例えば Twitter のフォローも多段階の関連付けによって実現されることになります。そのため、自己参照型リレーションによって、多段階の関連付けが容易に実現可能であることも是非覚えておいてください!

ManyToManyField における自己参照型リレーション

さて、ここまで主に ForeignKey をリレーションフィールドとした自己参照型リレーションについて解説してきました。

次は、ManyToManyField をリレーションフィールドとした自己参照型リレーションについて解説していきます。基本的には ManyToManyField の場合も ForeignKey の場合と同じ感覚で自己参照型リレーションを利用することも可能なのですが、注意が必要になるのが引数 symmetrical になります。

symmetrical=False を指定した場合は、前述の通り ForeignKey の場合と同じ感覚で自己参照型リレーションを利用することができます。が、symmetrical=True を指定した場合は、少し特殊なリレーションとなるため、ForeignKey と同じ感覚で自己参照型リレーションを利用することはできません。ManyToManyField をリレーションフィールドとする場合は、この点について注意をしながらモデルクラスを定義する必要があります。

ということで、ここでは ManyToManyField による自己参照型リレーションについて、特に symmetrical 引数に注目しながら解説をしていきたいと思います。

symmetrical とは

では、まずは、この引数 symmetrical について解説します。

この symmetricalManyToManyField のコンストラクタに指定可能な引数になります。

そして、この symmetrical は、自己参照型リレーションでの「対称な関連付け」を強制するかどうかを指定するための引数になります。symmetrical=True を指定した場合は対称な関連付けが強制されることになります。したがって、非対称な関連付けを実現したいのであれば、ManyToManyField のコンストラクタには symmetrical=False を指定する必要があります。

ちなみに、symmetrical のデフォルト値は、ManyToManyField のコンストラクタの第1引数に 'self' を指定している場合は True となり、第1引数に '自分自身のモデルクラス名' を指定している場合は False となります。ちょっと分かりにくいので基本的には明示的に引数へ symmetrical=False or symmetrical=True を指定するのが良いと思います。

非対称な関連付けと対称な関連付け

非対称な関連付けとは、2つのインスタンスの内、一方のインスタンスからのみ能動的(or 受動的)な関連付けが行われている状態のことを言います。

ここでは、下記の Student を例に挙げて「非対称な関連付け」について説明していきます。

非対称な関連付けを実現するモデルクラス
from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=32)
    loving = models.ManyToManyField('self', symmetrical=False, related_name='loved_by')

この Student では ManyToManyFieldsymmetrical=False を指定しているため、非対称な関連付けが実現可能です。そして、この場合は ForeignKey と同様の感覚、つまり、関連付けを行うと 能動的な関連付けと受動的な関連付け と同様の結果が得られることになります。

まず、loving を「好き」という能動的な関連付けとして考え、さらに studentAstudentBStudent のインスタンスとすると、下記を実行すれば studentA から studentB への関連付けを行うことができます。

非対称な関連付け
studentA.loving.add(studentB)

そして、この関連付けによって「studentA は studentB が好き」という関係性が生まれることになります。同時に、「studentB は studentA から好かれている」という関係性が生まれることになり、studentB.loved_by の集合に studentA が追加されることになります。

一方のインスタンスからの能動的な関連付けによって他方のインスタンスでの受動的な関連付けも自動的に実施される様子

なんですが、「studentB は studentA から好かれている」は、あくまでも「studentA は studentB が好き」を言い換えた言葉であり、関係性としては同じです。能動的な関係性で言えば、「studentA は studentB が好き」が成立しているだけで、「studentB は studentA が好き」という関係性はありません。この場合は、studentA の studentB への片思いという状態ということになります。

studentAからのみstudentBに能動的な関連付けが行われている様子

このような、一方のインスタンスからのみ能動的(or 受動的)な関連付けが行われている状態の関連付けのことを「非対称な関連付け」と呼びます。

非対称な関連付けの説明図

今度は下記の処理について考えてみましょう。

symmetrical=False時の対称な関連付け
studentA.loving.add(studentB)
studentA.loved_by.add(studentB)

この場合、studentAloving によって studentB が関連付けられ、さらに studentB の loving によって studentA が関連付けられるため、「studentA は studentB が好き」と「studentA は studentB から好かれている」の2つの関連性が生まれることになります。また、後者に関しては、能動的な関連付けで考えると「studentB は studentA が好き」という関係性と捉えられます。つまり、この場合、studentA から studentB が能動的に関連付けられ、さらに studentB から studentA が能動的に関連付けられていることになります。つまり、この場合は両想いということになります。

studentAとstudentBの両方から他方への能動的な関連付けが行われている様子

このような、双方のインスタンスからの両方で能動的(or 受動的)な関連付けが行われている状態の関連付けのことを「対称な関連付け」と呼びます。

対称な関連付けの説明図

このように、能動的 or 受動的な関連付けの一方にのみ着目し、その関連付けが両方向から行われていれば「対称な関連付け」となりますし、一方向から行われていれば「非対称な関連付け」となります。

MEMO

互いに関連付けが行われていない場合も「対象な関連付け」に含まれます

そして、前述の通り、symmetrical は ManyToManyField への引数の1つであり、自己参照型リレーションでの「対称な関連付け」を強制するかどうかを指定するための引数になります。symmetrical=False が指定されている場合、自己参照型リレーションでの「対称な関連付け」は強制されません。

つまり、symmetrical=False を指定した場合、非対称な関連付け、すなわち一方のインスタンスからのみ能動的(or 受動的)な関連付けが行われている状態の関連付けが実現可能ですし、対称な関連付け、すなわち双方のインスタンスからの両方で能動的(or 受動的)な関連付けが行われている状態の関連付けも実現可能です。

symmetrical=Falseの場合に実現可能な関連付けを示す図

このように、非対称・対象の両方の関連付けが実現できるのが symmetrical=False の効果ということになります。

それに対し、symmetrical=True を指定した場合、もしくは symmetrical 引数を指定しなかった場合は、対称な関連付けが強制されることになります。つまり、一方のインスタンスから非対称な関連付けを行うと、対称な関連付けとなるよう、自動的に他方側のインスタンスからの関連付けが行われることになります。そのため、この場合は、一方のインスタンスからのみ能動的(or 受動的)な関連付けが行われている状態が存在しないことになります。

symmetrical=Trueの場合に実現可能な関連付けを示す図

ここからは、下記の Student を例にして、symmetrical=True の効果を確認したいと思います。

非対称な関連付けを禁止するモデルクラス
from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=32)
    loving = models.ManyToManyField('self', symmetrical=True)

先ほどと同様に、loving を能動的な関連付けとして考え、さらに studentAstudentBStudent のインスタンスとすると、下記を実行すれば studentA から studentB への関連付けを行うことができます。

symmetrical=True時の対称な関連付け
studentA.loving.add(studentB)

ですが、この状態だと、能動的な関連付けに関しては studentA から studentB の一方向に対してのみ関連付けが行われていることになり、非対称な関連付けとなっています。

そのため、symmetrical=True を指定している場合は、この非対称な関連付けを解消するため、studentB.loving の集合への studentA の追加、すなわち studentB から studentA への能動的な関連付けも自動的に行われることになります。つまり、これによって能動的な関連付け、すなわち loving での関連付けが両方向から行われた状態、すなわち対称な関連付けとなります。

symmetrical=Trueの効果を示す図

また、対象な関連付けの状態で、特定のインスタンスから相手のインスタンスへの関連付けが解除されたときには、相手のインスタンス側からの特定のインスタンスへの関連付けも自動的に解除されることになります。このように、symmetrical=True を指定している場合、常に非対称な関連付けにならないよう関連付けに対する制御が自動的に実施されるようになっています。

一方からの関連付けが解除されると、対称性を保つために他方側の関連付けも自動的に解除される様子

このように、非対称な関連付けが実施された際に、非対称な関連付けのまま維持されるのが symmetrical=False を指定した効果であり、非対称な関連付けが実施された際に、対称な関連付けとなるよう自動的に関連付けに関する制御が実施されるのが symmetrical=True の効果となります。 

スポンサーリンク

symmetrical=True 指定時のデータ属性

また、ここまで、自己参照型リレーションフィールドを定義することで、インスタンスに2つのデータ属性が追加されると説明してきましたが、リレーションフィールドが ManyToManyField、かつ、引数に symmetrical=True が指定されている場合は、そのモデルクラスのインスタンスに追加されるデータ属性は1つのみとなります。リレーションフィールドのフィールド名のデータ属性のみが追加されることになります。

例えば、下記のように Student を定義した場合、Student のインスタンスには loving のデータ属性のみが追加されることになります。

非対称な関連付けを実現するモデルクラス
from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=32)
    loving = models.ManyToManyField('self', symmetrical=True)

追加されるデータ属性が1つとなる理由は、symmetrical=True が指定されている場合、もはや能動的な関連付けの管理と受動的な関連付けの管理を区別して行うことが無意味となるためです。

先ほど、symmetrical=True が指定されている場合、特定のインスタンスから相手に対して能動的な関連付けを行えば、対称な関連付けを維持するため、自動的に相手からその特定のインスタンスに対する能動的な関連付けが行われると説明しました。この自動的に実施される関連付けを受動的な観点で考えた場合、これは、その特定のインスタンスからの相手に対する受動的な関連付けとなります。

symmetrical=Trueの場合、能動的な関連付けを行うと受動的な関連付けも自動的に実施されることを示す図

つまり、特定のインスタンスから能動的な関連付けを行えば、同時に同じ相手との受動的な関連付けも行われることになります。したがって、symmetrical=True が指定されている場合、各インスタンスにおいては能動的に関連付けられたインスタンスと受動的に関連付けられたインスタンスとが常に同じになることになります。

symmetrical=Trueを指定している場合、能動的な関連付けを行なったインスタンスと受動的な関連付けを行なったインスタンスとが同じになることを示す図

能動的に関連付けられたインスタンスと受動的に関連付けられたインスタンスとが常に同じなのであれば、もはやそれらを区別して管理する必要はありません。そのため、リレーションフィールドが ManyToManyField、かつ、引数に symmetrical=True が指定されている場合、能動的な関連付けを管理するデータ属性と受動的な関連付けを管理するデータ属性の2つが追加されるのではなく、単なる関連付けを管理するデータ属性が1つのみ追加されることになります。

symmetrical=Trueを指定したManyToManyFieldを定義した場合は追加されるデータ属性が1つのみであることを示す図

また、自己参照型リレーションフィールドとして ManyToManyField のフィールドを定義する場合、symmetrical=True を指定した状態で引数 related_name を指定すると、ウェブアプリ起動時等に下記のような警告文が表示されることになります。これは、前述の通り、この場合は related_name によって名付けられるデータ属性が追加されないため、意味のない引数指定となるからになります。

WARNINGS:
lovers.Student.lovings: (fields.W345) related_name has no effect on ManyToManyField with a symmetrical relationship, e.g. to "self".

symmetrical=Falsesymmetrical=True の使い分け

最後に、symmetrical=Falsesymmetrical=True の使い分けについて説明しておきます。

ここまでの説明の通り、ManyToManyField を利用して自己参照型リレーションを実現する場合、symmetrical=False を指定しないと非対称な関連付けを実現することができません。なので、非対称な関連付けを行いたいのであれば symmetrical=False の指定がマストとなります。

それに対し、対称な関連付けは symmetrical=False でも symmetrical=True でも実現可能です。ですが、非対称な関連付けが不要な場合 or 常に対象な関連付けである必要がある場合は symmetrical=True を利用する方が良いと思います。ここまで説明してきたように、symmetrical=True を指定すれば追加されるデータ属性が1つのみとなるので、コーディングが楽&シンプルになります。また、関連付けの漏れなども発生しにくくなります。つまり、symmetrical=True を指定することで開発効率が向上し、さらにバグも減らすことができます。そのため、非対称な関連付けが不要なのであれば symmetrical=True を指定することをオススメします。

例えば、前述の Student の場合であれば、一方の生徒が好きになったからといって、その生徒が相手からも好きになってもらえるとは限りません。そのため、現実世界に合わせたモデルクラスを定義したいのであれば symmetrical=False を指定するのが自然だと思います。ですが、例えば Student を利用して生徒の相思相愛の関係性だけを管理したいのであれば、symmetrical=True を指定するのが良いと思います。

また、他の例として、私がよく遊んでいたパワサカというアプリには(サ終になってしまいました…)、他のユーザーをフォローする「フォロー機能」と他のユーザーとフレンドになる「フレンド機能」が存在しています。

「フォロー機能」は Twitter などと同じで、特定のユーザーが他のユーザーに対して一方向的に関係性を持たせるものになります。私が他のユーザーに対してフォローを行ったとしても、相手が私をフォローしてくれるとは限りません。つまり、このようなフォロー機能を実現するためには非対称な関連付けが必要となります。

フォロー機能の説明図

それに対して「フレンド機能」の場合は、相手にフレンド申請を出し、承認された場合のみ、お互いにフレンドになれる機能となります。承認さえされれば、相手から見ても自分から見てもフレンドの関係となります。

フレンド機能の説明図

また、フレンド関係を解消するようなことも可能で、一方からフレンドが解消されれば、他方からのフレンド関係も解消されるようになっています。つまり、このフレンド関係においては非対称な関連付けは存在しません。

一方からリレーションが解消された場合の説明図

したがって、上記のようなフォロー機能を実現したい場合は symmetrical=False を指定する必要があります。それに対し、フレンド機能を実現したい場合は symmetrical=True を指定した方がコーディングが楽&シンプルになります。ですので、このようなフォロー機能とフレンド機能を実現する際には、例えば下記のようにモデルクラスを定義することになると思います。

フォロー機能とフレンド機能
class User(models.Model):
    name = models.CharField(max_length=32)
    followings = models.ManyToManyField('self', symmetrical=False, related_name='followed_by')
    friends = models.ManyToManyField('self', symmetrical=True)

結局重要なのは、開発対象のウェブアプリや機能において、インスタンス同士の非対称な関連付けが必要かどうかを考え、それに応じて symmetrical=Falsesymmetrical=True を適切に選択することになります。そのためにも、非対称な関連付けと対称な関連付けの特徴、および、symmetrical=Falsesymmetrical=True の違いについてはしっかり理解しておきましょう!

まとめ

このページでは、Django における自己参照型リレーションについて解説を行いました。

自己参照型リレーションは、同じモデルクラスのインスタンス同士の関連付けのことで、リレーションフィールドの第1引数に '自分自身のモデルクラス名' もしくは 'self' を指定することで自己参照型リレーションを実現することができます。

そして、自己参照型リレーションを利用することで、下記のような機能を Django で簡単に実現することができるようになります。

  • コメント返信
  • フォロー
  • フレンド

同じモデルクラスのインスタンス同士を関連付けたくなるケースも多いですし、自己参照型リレーションの場合は、多段階の関連付けも簡単に実現することができ、この多段階の関連付けを利用するためにも利用されることが多いです。

いろんな場面で利用する機会のあるリレーションとなりますので、是非このページで解説した内容に関しては頭に入れておいていただければと思います!

同じカテゴリのページ一覧を表示

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です