概要
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
僕から以上