4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

何が出来るようになるか

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を選定しました。少しでも皆さんのお役に立てると幸いです。

4
3
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
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?