【Python】Djangoで複数モデル間でリレーションの値を取得する

こんにちは、nishi_talk(@nishi_talk)です。
システムを開発していると複数モデルを作成します。ひとつの投稿に対して、さまざまな情報を紐付けることが可能です。今回はDjango(ジャンゴ)で複数モデル間でリレーションを取得して関連する値を取得する方法をご紹介します。

まずはモデル

項目はざっくりイメージ。
class Company(models.Model):
    name = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
 
class Post(models.Model):
    name = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    company = models.ForeignKey(Company, verbose_name='投稿', on_delete=models.CASCADE)
 
class Post_detail(models.Model):
    body = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    post = models.ForeignKey(Post, verbose_name='投稿詳細', on_delete=models.CASCADE)
リレーションはこんな感じで設定しました。
(1)Company <-> (多)Post(1) <-> (多)Post_detail
データベースはこんな感じ
mysql> show columns from company;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| name       | varchar(255) | NO   |     | NULL    |                |
| created_at | datetime(6)  | NO   |     | NULL    |                |
| updated_at | datetime(6)  | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
 
mysql> show columns from post;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| created_at | datetime(6)  | NO   |     | NULL    |                |
| updated_at | datetime(6)  | NO   |     | NULL    |                |
| company_id | int(11)      | NO   | MUL | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
 
mysql> show columns from post_detail;
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| id         | int(11)     | NO   | PRI | NULL    | auto_increment |
| created_at | datetime(6) | NO   |     | NULL    |                |
| updated_at | datetime(6) | NO   |     | NULL    |                |
| post_id    | int(11)     | NO   | MUL | NULL    |                |
+------------+-------------+------+-----+---------+----------------+

CompanyのPostを取得

# view.pyに記述
from .models import Company, Post, Post_detail
post = Post.objects.filter(company_id__exact=1).all()
Postのオブジェクトからcompany_idが1のオブジェクトを取得します。
company_idの後にField lookupsを指定する。exactは一致するって意味みたい。
Field lookupsの説明はドキュメントを参考に。 QuerySet API reference

Companyからpost_detailを取得する

# view.pyに記述
from .models import Company, Post, Post_detail
post_detail = Post_detail.objects.filter(post__company__id=1).all()
Post_detailのオブジェクトからcompany_idが1のオブジェクトを取得します。
filter()内は 「table_name」「table_name」「column_name」で複数テーブルをまたがってJoin。
上記の例だと「Post → Company → Company内のID」って感じでjoinしてる。

参考記事

SQLのSELECT文を、DjangoのQuerySet APIで書いてみた