2018/07/29
Django モデル クエリセットrelated_nameの存在意義とは?
DjangoでForeignKeyを使う時は、オプションの引数としてrelated_nameを指定することができます。また、状況によっては必ず指定しなくてはいけない場面があります。
related_nameはどういう働きをしているのか、説明したいと思います。まずは、ForeignKeyを使って、FoodモデルとPersonモデルを紐づけるところからです。
models.py
from django.db import models
class Food(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Person(models.Model):
name = models.CharField(max_length=100)
favorite_food = models.ForeignKey(Food, on_delete="CASCADE", null=True)
def __str__(self):
return self.name
上のようにfavorite_foodフィールドでForeignKeyを使うことによってFoodとPersonが結びつき、以下のように参照と逆参照ができるようになります。
参照と逆参照
# 参照
>>> person1 = Person.objects.get(name="ジョブス")
>>> person1.favorite_food
<Food: 寿司>
# 逆参照
>>> food1 = Food.objects.get(name="寿司")
>>> food1.person_set.all()
<QuerySet [<Person: ジョブス>, <Person: ローラ>, <Person: ジョン>]>
person1.favorite_food
のように、インスタンス.フィールド名とすることで参照ができ、上記ではジョブスの好きな食べ物を取得しています。
また、food1.person_set
のように、インスタンス.クラス名_setとすることで逆参照でき、上記では寿司が好きな人の一覧をクエリセットとして取得しています。
複数のフィールドでForeignKeyを使う
ここまでは、単純なForeignKeyの使い方です。では、Personクラス内で、favorite_foodフィールドとは別にもう一つFoodを参照するフィールドを作成したらどうなるでしょう。嫌いな食べ物を保存するフィールドとして、hate_foodを追加してみます。
Personクラスは以下のようになります。
models.py
class Person(models.Model):
name = models.CharField(max_length=100)
favorite_food = models.ForeignKey(Food, on_delete="CASCADE", null=True)
hate_food = models.ForeignKey(Food, on_delete="CASCADE", null=True)
def __str__(self):
return self.name
このようにすると、以下のようにエラーが出てしまいます。
エラー
ERRORS:
app.Person.favorite_food: (fields.E304) Reverse accessor for 'Person.favorite_food' clashes with reverse accessor for 'Person.hate_food'.
HINT: Add or change a related_name argument to the definition for 'Person.favorite_food' or 'Person.hate_food'.
app.Person.hate_food: (fields.E304) Reverse accessor for 'Person.hate_food' clashes with reverse accessor for 'Person.favorite_food'.
HINT: Add or change a related_name argument to the definition for 'Person.hate_food' or 'Person.favorite_food'.
System check identified 2 issues (0 silenced).
簡単に言うと、この記述じゃfavorate_foodとhate_foodに対して逆参照することができないよと言っています。
最初の例だと、food1.person_set
とすることで逆参照することができましたが、Foodを参照しているフィールドが複数あると、person_set
と記述しても、「寿司を好きな人の一覧」なのか「寿司を嫌いな人の一覧」なのか、どちらのことを指しているのかがわかりませんよね。
related_nameの使い方
これを解決するためにあるのが、related_nameです。
それぞれのフィールドにrelated_nameを指定しておき、逆参照するときはクラス名ではなくrelated_nameを使うことで逆参照が可能となります。
models.py
class Person(models.Model):
name = models.CharField(max_length=100)
favorite_food = models.ForeignKey(Food, on_delete="CASCADE", null=True, related_name = "favorite_people")
hate_food = models.ForeignKey(Food, on_delete="CASCADE", null=True, related_name = "hate_people")
def __str__(self):
return self.name
上のようにそれぞれのフィールドにrelated_nameを与えることで、以下のように逆参照することができます。
related_nameを使った逆参照
>>> food1 = Food.objects.get(name="寿司")
# 寿司が好きな人の一覧
>>> food1.favorite_people.all()
<QuerySet [<Person: ジョブス>, <Person: ローラ>, <Person: ジョン>]>
# 寿司が嫌いな人の一覧
>>> food1.hate_people.all()
<QuerySet [<Person: ケイ>]>
以上のように、複数のフィールドで同じモデルを参照する場合は、related_nameをつけるようにしましょう。