【Django】親モデルから子モデルへ逆参照する方法【prefetch_related】

  • Djangoで親モデルから子モデルへ逆参照するには?
  • 「prefetch_related」の使い方とは?

本記事ではこのような疑問を解決します。


モデルを扱う時に、ForeignKeyを使って親となるモデルからデータを参照することはよくあります。

いわゆる、参照(子モデル→親モデル)というやつです。

一方、親モデルから子モデルを参照する逆参照(親モデル→子モデル)というものもあります。


そこで今回はこの逆参照(親モデル→子モデル)のやり方について解説していきます。

親モデル・子モデル間の参照と逆参照

ここでいま一度、参照と逆参照について確認してみましょう。

○参照
ある子モデルがどの親モデルに紐づいているか確認する
→子モデル中のForeignKeyから、親モデルの情報を取得する


○逆参照
ある親モデルにはどの子モデルが紐づいているか確認する
→親モデルから、ForeignKeyで自身を参照している子モデルの情報を取得する

逆参照(親モデル→子モデル)する方法

まずは、models.pyにて以下のモデルを定義します。

# 親モデル
class MainCategory(models.Model):
    class Meta:
        verbose_name = "メインカテゴリー"
        verbose_name_plural = verbose_name
    
    main_category_id = models.IntegerField(primary_key=True, unique=True)
    title = models.CharField(max_length=50)


# 子モデル
class SubCategory(models.Model):
    class Meta:
        verbose_name = "サブカテゴリー"
        verbose_name_plural = verbose_name
    
    sub_category_id = models.IntegerField(primary_key=True, unique=True)
    title = models.CharField(max_length=50)
    main_category = models.ForeignKey(MainCategory, on_delete=models.CASCADE, related_name='sub_category')

ここでのポイントは子モデル中のForeignKeyフィールドに「related_name」を追加することです。

これによって、逆参照(親モデル→子モデル)する準備が整います。

なお、「related_name」は自身のモデル名を付けるとわかりやすいです。



モデルを定義したら、views.pyにて逆参照してみましょう。

逆参照には「prefetch_related」というメソッドを使用します。

言葉で説明するより、実際にコードを見た方がわかりやすいので下記の記述を確認しましょう。

# ①親モデルのデータと一緒に紐づいている子モデルのデータをキャッシュする
main_category = MainCategory.objects.filter(main_category_id=1).prefetch_related('sub_category')
# ポイント
# ・ここで「print(main_category)」としても、親モデルのデータのみ出力される
# ・QuerySetで返す必要があるため、レコードが1つしかなくてもget()ではなくfilter()を使う


# ②親モデルのデータを1つ取得し、紐づいている子モデルのデータを参照する
sub_category = main_category[0].sub_category.all()
# ポイント
# ・related_nameで付けた名前で子モデルのレコードを参照する

これで逆参照(親モデル→子モデル)ができます。

親モデルから孫モデルまで逆参照する方法

ここまで説明してきた逆参照は「親モデルから子モデルへ」のやり方でした。

一方、逆参照は「親モデルから孫モデルへ」のやり方もあります。

まずは、先ほどのmodels.pyに孫モデルになり得るものを下記の通り追加します。

# 親モデル
class MainCategory(models.Model):
    class Meta:
        verbose_name = "メインカテゴリー"
        verbose_name_plural = verbose_name
    
    main_category_id = models.IntegerField(primary_key=True, unique=True)
    title = models.CharField(max_length=50)


# 子モデル
class SubCategory(models.Model):
    class Meta:
        verbose_name = "サブカテゴリー"
        verbose_name_plural = verbose_name
    
    sub_category_id = models.IntegerField(primary_key=True, unique=True)
    title = models.CharField(max_length=50)
    main_category = models.ForeignKey(MainCategory, on_delete=models.CASCADE, related_name='sub_category')


# 孫モデル
class Item(models.Model):
    class Meta:
        verbose_name = "商品"
        verbose_name_plural = verbose_name
    
    item_id = models.IntegerField(primary_key=True, unique=True)
    title = models.CharField(max_length=50)
    price = models.IntegerField()
    sub_category = models.ForeignKey(SubCategory, on_delete=models.CASCADE, related_name='item')

そして、先ほどと同じようにprefetch_relatedを使って「親モデルから孫モデルへ」の逆参照をしてみます。

# ①親モデルのデータと一緒に紐づいている子モデルのデータをキャッシュする
main_category = MainCategory.objects.filter(main_category_id=1).prefetch_related('sub_category')
# ポイント
# ・ここで「print(main_category)」としても、親モデルのデータのみ出力される
# ・QuerySetで返す必要があるため、レコードが1つしかなくてもget()ではなくfilter()を使う


# ②親モデルのデータを1つ取得し、紐づいている子モデルのデータを参照する
sub_category = main_category[0].sub_category.all()
# ポイント
# ・related_nameで付けた名前で子モデルのレコードを参照する


# ---------------- ここから先ほどの続き ----------------

# ③子モデルのデータを1つ取得し、紐づいている孫モデルのデータを参照する
item = sub_category[0].item.all()
# ポイント
# ・「親モデル→子モデル」のやり方と同じ

これで親モデルから孫モデルまでの逆参照ができるようになりました。

まとめ

以上がDjangoにおける逆参照(親モデルから子モデルへ)の方法になります。

今回のポイントは以下の通り。

・子モデル中のForeignKeyフィールドに「related_name」を追加する

・逆参照には「prefetch_related」というメソッドを使用する

本記事が、少しでもStripeを用いたWebアプリ開発の手助けになれれば幸いです。

実務1年未満でもOK!フリーランスエンジニアの案件獲得方法はこちら!

必読

フリーランスエンジニアが案件獲得方法とは?自ら営業せずに案件を獲得するには?実務経験1年未満でも大丈夫なの? 本記事ではこのような疑問を解決します。これからフリーランスエンジニアとして独立したい方は、兎にも角にも案件の獲得が急務です[…]

アイキャッチ画像