0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Django】BookモデルからAuthorDetailの情報を取得する2つの方法(related_name、select_related、prefetch_related)

Posted at

概要

Djangoにて、BookAuthorAuthorDetailが定義されたモデルがあるとします。

この時、Bookモデルへのアクセス一回で著者のbiographyの値まで取得するにはどうすればいいでしょうか。
Django ORMで用意されたselect_relatedと外部キーを利用することで、簡単にAuthorDetailモデルまでアクセスすることができます。

今回は2通りの方法を紹介します。

models.pyrelated_nameについて

models.pyは以下の前提です。

models.py
class Book(models.Model):
    book_id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=255, null=True)
    author_id = models.ForeignKey('Author', on_delete=models.CASCADE, null=True, db_column='author_id')
    genre_id = models.ForeignKey('Genre', on_delete=models.CASCADE, null=True, db_column='genre_id')

    class Meta:
        db_table = 'book'


class Author(models.Model):
    author_id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255, null=True)

    class Meta:
        db_table = 'author'


class Genre(models.Model):
    genre_id = models.AutoField(primary_key=True)
    genre_name = models.CharField(max_length=100, null=True)

    class Meta:
        db_table = 'genre'


class AuthorDetail(models.Model):
    author_detail_id = models.AutoField(primary_key=True)
    author_id = models.ForeignKey(Author, related_name='author_details', on_delete=models.CASCADE, null=True, db_column='author_id')
    biography = models.TextField(null=True)

    class Meta:
        db_table = 'author_detail'

補足すると、related_nameはdjangoのmodel間のリレーションシップにおいて逆方向のリレーションを参照するための名前を指定するために使用されます。関連フィールドが簡単に取得できますよ、というもの。

念の為、注意点としては、同じmodel内ではrelated_nameの名前は一意である必要があります。複数のフィールドに対して同じ名前を指定することができません。
また、特に指定しない場合は<model_name>_setという名前がデフォルトです。

それでは、主題の「Bookモデルからbiographyを取得する」方法について考えてみます。

私がDjango5.1で検証したところ、以下の2通りで実現できました。

related_nameを利用してフィールドにアクセスする方法

views.py
from book.models import Book

## 省略 ##

query_set = Book.objects.all()
data = query_set.select_related(
       'author_id',
       'genre_id'
       ).prefetch_related(
       'author_id__author_details'
       ).values(
       'book_id',
       'title',
       'author_id',
       'genre_id',
       'author_id__author_details__biography'
       )

models.pyで定義したrelated_nameを使用して関連オブジェクトにアクセスする際は、select_relatedではなくprefetch_relatedを使用する必要があります。

それぞれ以下の違いがあります。

select_related:外部キーを持つ単一の関連オブジェクトに対して使用
prefetch_related:多対多や逆方向の外部キーに対して使用

related_nameは逆方法の外部キーに対して使用されるものなので、prefetch_relatedで取得します。

繋がったルートを感覚的に書くとこんな感じです。

「(Bookの)フィールド名__(そのカラムを持つ親モデルの中で定義された)リレーション名__(そのモデルが持つ)フィールド名」

prefetch_relatedを使用することで、関連オブジェクトを効率的に取得できる(データベースクエリを減らすことができる)ので、パフォーマンス重視の際はこちらの方が良いのではないでしょうか。

②モデル名を直接利用してフィールドにアクセスする方法

views.py
from book.models import Book,

## 省略 ##

query_set = Book.objects.all()
data = query_set.select_related(
       'author_id',
       'genre_id'
       ).values(
       'book_id',
       'title',
       'author_id',
       'genre_id',
       'author_id__authordetail__biography'
       )

上記の方法でもアクセス可能でした。

この場合は、authordetailと指定しているように、モデル名を直接使用しています。

「(Bookの)フィールド名__(そのカラムを持つ親の)モデル名__(そのモデルが持つ)フィールド名」

という感じなので、直感的にはこちらの方がルックアップのチェーンっぽくて読みやすい気もしますね。

ただし、こちらの場合はprefetch_relatedを使用していないため、関連オブジェクトの取得が非効率になることがあります。モデル名が長くなると何が書いてあるのかわかりづらいこともありますし。
ちなみにここではbiographyを指定していますが、デフォルトでは主キーになっているようです。

以上、DjangoのORMでリレーションシップを利用して、DBアクセス一回で別のモデルの情報を取得する方法でした。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?