Django 外部キーのフィールドに設定するキー名を変更する方法


何が出来るようになるか

Djangoで一対多のリレーションをJSONで返却するAPIを作成した場合、以下の様なレスポンスが返ってくると思います。この時、外部キーで参照される値のキー名employee_setを別名(employees)に変更する方法を紹介します。

レスポンス(キー名変更前)

キー名 employee_setemployeesにしたい。


[
    {
        "id": "157b2b72-8315-446d-8120-07b408871ef3",
        "name": "ABC",
        "employee_set": [                      # キー名を変更したい
            {
                "name": "やまだ"
            },
            {
                "name": "すずき"
            }
        ]
    },
    {
        "id": "5ce3f3fe-7d3c-41c3-b3c0-f11d7d26d59f",
        "name": "XYZ",
        "employee_set": [                                        # キー名を変更したい
            {
                "name": "たなか"
            },
            {
                "name": "たなべ"
            }
        ]
    }
]

環境

  • Python: Python 3.7.1
  • Django: 2.2.5
  • djangorestframework: 3.10.3

Model

  • 会社(1):従業員(多)の簡単なモデルを作成します
models.py
from django.db import models
import uuid


class Company(models.Model):
    """会社テーブル"""

    class Meta:
        db_table = 'company'

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(verbose_name="会社名", max_length=30)

    def __str__(self):
        return self.name


class Employee(models.Model):
    """従業員テーブル"""

    class Meta:
        db_table = 'employee'

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(verbose_name="氏名", max_length=30)
    company = models.ForeignKey(Company, on_delete=models.CASCADE, null=False)

    def __str__(self):
        return self.name

Serializer

  • Djangoでは以下のルールがあります
    1. ForeignKeyを設定したモデルには、関連先のモデルの主キーを扱うために「ForeignKey のフィールド名」_idが付与される
      • 今回の場合は、EmployeeモデルからCompanyモデルを参照する
    2. ForeignKeyを設定したモデルには、関連先のモデルを参照するために`「ForeignKey のフィールド名」で参照できる
    3. ForeignKeyが設定されていないモデルでは、逆参照をするために「逆参照先のモデルクラス名(小文字)_set`で逆参照できる
      • 今回の場合は、CompanyモデルからEmployeeモデルを参照する

上記のルールに従って、CompanyのオブジェクトにEmployeeのオブジェクトを入れ子にして返却する場合は、以下の実装になります。その場合、CompanySerializerクラスに上記のルール「3」に従って、employee_setという変数名を利用して、fieldsの配列に設定する必要があります。

serializers.py
from rest_framework import serializers
from .models import Company, Employee


class EmployeeSerializer(serializers.ModelSerializer):
    class Meta:
        model = Employee
        fields = ['name']


class CompanySerializer(serializers.ModelSerializer):
    employee_set = EmployeeSerializer(many=True)

    class Meta:
        model = Company
        fields = ['id', 'name', 'employee_set']

View

ModelViewSetを継承するだけでサクッとCRUDが作成できるのは本当に助かります;;

views.py
from rest_framework import viewsets
from .models import Company, Employee
from .serializers import CompanySerializer, EmployeeSerializer


class CompanyViewSet(viewsets.ModelViewSet):
    queryset = Company.objects.all()
    serializer_class = CompanySerializer


class EmployeeViewSet(viewsets.ModelViewSet):
    queryset = Employee.objects.all()
    serializer_class = EmployeeSerializer

変更方法

2つ方法があります。

  • serialziers.pyを変更する方法
  • models.pyを変更する方法(本記事を記載中、周辺調査の際に見つけました)
    • serializers.pyも変更が必要

serialziers.pyを変更して適用する方法

serializers.py
from rest_framework import serializers
from .models import Company, Employee


class EmployeeSerializer(serializers.ModelSerializer):
    class Meta:
        model = Employee
        fields = ['name']


class CompanySerializer(serializers.ModelSerializer):
    # employee_set = EmployeeSerializer(many=True)
    employees = EmployeeSerializer(many=True, source='employee_set')
    class Meta:
        model = Company
        fields = ['id', 'name', 'employees']

models.pyを変更して適用する方法

外部キーを作成するForeignKeyrelated_name="キー名"を指定します。

models.py
from django.db import models
import uuid


class Company(models.Model):
    """会社テーブル"""

    class Meta:
        db_table = 'company'

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(verbose_name="会社名", max_length=30)

    def __str__(self):
        return self.name


class Employee(models.Model):
    """従業員テーブル"""

    class Meta:
        db_table = 'employee'

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(verbose_name="氏名", max_length=30)
    company = models.ForeignKey(Company, on_delete=models.CASCADE, null=False, related_name="employees") # related_nameを指定する

    def __str__(self):
        return self.name
serialziers.py
from rest_framework import serializers
from .models import Company, Employee


class EmployeeSerializer(serializers.ModelSerializer):
    class Meta:
        model = Employee
        fields = ['name']


class CompanySerializer(serializers.ModelSerializer):
    employees = EmployeeSerializer(many=True)

    class Meta:
        model = Company
        fields = ['id', 'name', 'employees']

レスポンス(変更後)

キー名がemployee_setからemployeesに変更されていることが確認できます。

[
    {
        "id": "157b2b72-8315-446d-8120-07b408871ef3",
        "name": "ABC",
        "employees": [
            {
                "name": "やまだ"
            },
            {
                "name": "すずき"
            }
        ]
    },
    {
        "id": "5ce3f3fe-7d3c-41c3-b3c0-f11d7d26d59f",
        "name": "XYZ",
        "employees": [
            {
                "name": "たなか"
            },
            {
                "name": "たなべ"
            }
        ]
    }
]

最後に

DjangoのRestFrameworkを使って返却されるJSONのキー名を変更する方法を紹介しました。Web系のフレームワーク作るならRailsの方が日本語の情報があったり、特に工夫せずにパフォーマンスを上げるならGoなどの選択肢があると思いますが、私の所属している業界では何かと周辺ツールがPythonのライブラリを提供してることが多く、趣味でも活かせそうなのでDjangoを選定しました。少しでも皆さんのお役に立てると幸いです。