LoginSignup
10
11

More than 5 years have passed since last update.

Django Rest Framework で親子関係のあるテーブルにデータを登録する

Posted at

はじめに

1つのエンドポイントで、親子関係があるテーブル構造に対してどうやってデータを登録するんだろうか。
業務中にそんな事があったので、記載します。

目次

1. 要件
2. リクエストAPIのデータ構造
3. モデルの定義
4. Viewの定義
5. Serializerの定義
6. URLディスパッチャの定義
7. 動作の確認
8. まとめ

1. 要件

今回は、「受注をする」という要件を想定して作業をしてみようと思います。
ECなどでよくある受注機能ですね。
受注ヘッダ(orderhead)、受注明細(orderdetail)という1対多の
親子関係を持ったテーブル構造に対してデータを登録することをゴールにします。

2. リクエストAPIのデータ構造

まずDjango側で受けるPOSTデータの定義をしようと思います。
以下のように、受注ヘッダの基となるデータ群(親)と、受注明細の基となるデータ群(子)を
定義しました。

order.json
{
    "order_number"  : "9000000",
    "customer_id" : "customer001",
    "order_detail": [
        {
            "product_code":"A001",
            "quantity":2
        },
        {
            "product_code":"A002",
            "quantity":2
        }
    ]
}

3. モデルの定義

データを格納するテーブル構造を以下に記載します。
受注ヘッダ(OrderHead)と受注明細(OrderDetail)です。
受注明細側には、Foreignキーを利用して受注ヘッダと受注明細に1対多の関係を築きます。

models.py
class OrderHead(models.Model):
    order_number = models.CharField(max_length=10)
    order_at = models.DateTimeField(auto_now_add = True)
    customer_id =  models.CharField(max_length=12)

class OrderDetail(models.Model):
    orderhead = models.ForeignKey(OrderHead, related_name='details', on_delete=models.CASCADE)
    product_code = models.CharField(max_length=10)
    quantity = models.PositiveSmallIntegerField()

4. Viewの定義

リクエストデータを受けたあとに、どう処理するかを記述します。
極力Django Rest frameworkの機能を活かそうとModelViewSetsを利用してます。

view.py

class OrderViewSet(viewsets.ModelViewSet):

    queryset = OrderHead.objects.all()
    serializer_class = OrderSerializer

    def create(self, request):

            order_serializer = OrderSerializer(data=request.data)

            if not order_serializer.is_valid():
                print(order_serializer.errors)
                return Response("validation error........")

            result = order_serializer.save()
            return Response(result)

5. Serializerの定義

view側からの呼び出しで、リクエストデータに対しての処理を記述します。
ModelSerializerを利用することでリクエストデータと、モデルフィールドのマッピングをして
バリデーションチェックやデータ登録処理を行うようにします。
※バリデーションは、modelフィールドに基づいて実施

order_haed、order_detailへデータ登録した際のサロゲートキー(自動採番キー)を取得して
レスポンスデータとして返却する仕組みにしました。

serializer.py


class OrderDetailSerializer(serializers.ModelSerializer):

    class Meta: 
        model = OrderDetail
        fields = ('product_code','quantity')


class OrderSerializer(serializers.ModelSerializer):

    order_detail = OrderDetailSerializer(many=True)

    class Meta:
        model = OrderHead
        fields = ('order_number','customer_id', 'order_detail')

    def create(self, validated_data):

        result_dict = {}
        detail_list = []

        order_detail = validated_data.pop('order_detail')

        created_orderhead = OrderHead.objects.create(**validated_data)
        result_dict['head_id'] = created_orderhead.id

        for order_detail_data in order_detail:

            created_orderdetail = OrderDetail.objects.create(orderhead=created_orderhead, **order_detail_data)
            detail_list.append(created_orderdetail.id)

        result_dict['detail_id'] = detail_list
        return result_dict

6. URLディスパッチャの定義(URLConf)

アプリケーション毎のurls.pyの設定は以下になります。
受注の操作を可能とするAPIなので、ordersを定義して、このURLに対して
HTTPメソッドを指定することで、動作を行うようにします。

urls.py
# coding: utf-8

from rest_framework import routers
from .views import OrderViewSet


router = routers.DefaultRouter()
router.register(r'orders', OrderViewSet)

7. 動作の確認

Postmanを利用して確認します。
レスポンスが返ってきていることが分かります。

response.png

続いてDBに登録されているか確認します。
oderheadテーブルのidがレスポンスで返ってきた値と一緒であることが分かります。

・orderheadテーブル確認
orderhead.png

・orderdetailテーブル確認
orderdetail.png

8. まとめ

受注ヘッダと受注明細にデータを登録するAPIを作成することができました。
作成して思ったことは、serializerは、OrderSerializerが1つあって、その中でOrderHeader,OrderDetailのserializerを読む形ができればもっと綺麗な形で
デザインできたんじゃないかと思います。
今日はここまで。
最後まで読んでくださった方 ありがとうございました。

10
11
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
10
11