疑念は探究の動機であり、探究の唯一の目的は信念の確定である。

数学・論理学・哲学・語学のことを書きたいと思います。どんなことでも何かコメントいただけるとうれしいです。特に、勉学のことで間違いなどあったらご指摘いただけると幸いです。 よろしくお願いします。くりぃむのラジオを聴くこととパワポケ2と日向坂46が人生の唯一の楽しみです。

Django REST Frameworkのモデルシリアライザーのフィールド名をハイフンに変更する方法

概要

Django REST Frameworkのモデルシリアライザー(serializers.ModelSerializer)のフィールド名にあるアンダースコア(_)をハイフン(-)に変更する方法をまとめる。



要点

モデルシリアライザーにハイフンを利用したい場合、シリアライザーのクラスMetaを次のように修正してください。

    class Meta:
        """Meta"""
        model = MODEL
        fields = ['hyphen-field']
        extra_kwargs = {
            'hyphen-field': {
                'source': 'model_field'
            }
        }



したいこと

  • 書店のデータベースを作成します(Store)
  • 登録されている書店を取得するAPIを作成します(GET: http://127.0.0.1:8000/api/stores/)
  • クエリがついていない場合は、すべての書店情報が取得されます。
  • クエリ(store-name)がついている場合は、クエリのバリデーションをおこない問題なければ、それを含む書店名の書店情報が取得されます。

背景

どうやらAPIのクエリはアンダースコアよりハイフンのほうが良いとのことです。しかしDjangoのモデルのフィールドはアンダースコアで記載されています。したがってクエリのバリデーションをおこなうモデルシリアライザーを作成したい場合、アンダースコアからハイフンに変更したいと思いました。

ソースコード

モデル

from django.db import models


class Store(models.Model):
    """Store model"""
    store_name = models.CharField(max_length=100)
    address = models.CharField(max_length=300)


リアライザー

from rest_framework import serializers
from backend.books.models.store import Store

class StoreSerializer(serializers.ModelSerializer):
    """Store serializer"""

    class Meta:
        """Meta"""
        model = Store
        fields = '__all__'


class StoreQuerySerializer(serializers.ModelSerializer):
    """Store query serializer"""

    class Meta:
        """Meta"""
        model = Store
        fields = ['store-name']
        extra_kwargs = {
            'store-name': {
                'source': 'store_name'
            }
        }


ビュー

from rest_framework import generics
from rest_framework.response import Response
from backend.books.serializer import StoreQuerySerializer, StoreSerializer


class StoreList(generics.ListAPIView):
    """Store list"""
    permission_classes = []
    serializer_class = StoreSerializer
    queryset = Store.objects.all()

    def list(self, request, *args, **kwargs):
        if not request.query_params:
            return super().list(request, *args, **kwargs)
        query_serializer = StoreQuerySerializer(data=request.query_params)
        query_serializer.is_valid(raise_exception=True)
        store_name = query_serializer.validated_data['store_name']
        queryset = Store.objects.filter(store_name__contains=store_name)
        return Response(data=self.get_serializer(queryset, many=True).data)


ソースコードについていくつか指摘をしたいと思います。

  • 最初のif文はクエリが設定されいるかどうかを確認しています。何もなければ通常のlist関数が実行されて、すべてのデータを取得します。
  • そうでない場合、つまりクエリが設定されている場合、StoreQuerySerializerでクエリのバリデーションをおこないます。query_serializer.is_valid(raise_exception=True)により、不適切なクエリの場合、エラーが返ります。
  • 適切なクエリが設定されている場合、バリデーションを通過します。そのときquery_serializer.validated_data['store_name']store-nameのクエリがあります。
  • あとはクエリに設定されたもの(store-name)を含むデータを絞り込んで返します。


URL

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from backend.books.views import StoreList

router = DefaultRouter()

urlpatterns = [
    path('stores/', StoreList.as_view()),
    path('', include(router.urls)),
]



実行

データベース

id store_name address
1 岩波書店 東京都千代田区一ツ橋2丁目5番5号
2 講談社 東京都文京区音羽 2-12-21
3 幻冬舎 東京都渋谷区千駄ケ谷四丁目9番7号
4 中央公論新社 東京都千代田区大手町1-7-1 読売新聞ビル19階
5 筑摩書房 東京都台東区蔵前2-5-3
6 みすず書房 東京都文京区本郷2-20-7



結果

クエリなし

期待値はすべてのStoreデータが取得されることです。

  • メソッド: GET
  • リソース: http://127.0.0.1:8000/api/stores/

[
    {
        "id": 1,
        "store_name": "岩波書店",
        "address": "東京都千代田区一ツ橋2丁目5番5号"
    },
    {
        "id": 2,
        "store_name": "講談社",
        "address": "東京都文京区音羽 2-12-21"
    },
    {
        "id": 3,
        "store_name": "幻冬舎",
        "address": "東京都渋谷区千駄ケ谷四丁目9番7号"
    },
    {
        "id": 4,
        "store_name": "中央公論新社",
        "address": "東京都千代田区大手町1-7-1 読売新聞ビル19階"
    },
    {
        "id": 5,
        "store_name": "筑摩書房",
        "address": "東京都台東区蔵前2-5-3"
    },
    {
        "id": 6,
        "store_name": "みすず書房",
        "address": "東京都文京区本郷2-20-7"
    }
]


クエリあり

成功ケース

  • メソッド: GET
  • リソース: http://127.0.0.1:8000/api/stores/
  • クエリ: store-name=書房

[
    {
        "id": 5,
        "store_name": "筑摩書房",
        "address": "東京都台東区蔵前2-5-3"
    },
    {
        "id": 6,
        "store_name": "みすず書房",
        "address": "東京都文京区本郷2-20-7"
    }
]


失敗ケース

クエリにアンダースコアがついている場合は、エラーとなります。

  • メソッド: GET
  • リソース: http://127.0.0.1:8000/api/stores/
  • クエリ: store_name=岩波書店

{
    "store-name": [
        "この項目は必須です。"
    ]
}

GitHub - YoheiWatanabe/recording-books at test-serializer-convert-underscore-into-hyphen







僕から以上