概要
Djangoにて、Book
、Author
、AuthorDetail
が定義されたモデルがあるとします。
この時、Book
モデルへのアクセス一回で著者のbiography
の値まで取得するにはどうすればいいでしょうか。
Django ORMで用意されたselect_related
と外部キーを利用することで、簡単にAuthorDetail
モデルまでアクセスすることができます。
今回は2通りの方法を紹介します。
models.py
とrelated_name
について
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
を利用してフィールドにアクセスする方法
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
を使用することで、関連オブジェクトを効率的に取得できる(データベースクエリを減らすことができる)ので、パフォーマンス重視の際はこちらの方が良いのではないでしょうか。
②モデル名を直接利用してフィールドにアクセスする方法
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アクセス一回で別のモデルの情報を取得する方法でした。