Django REST FrameworkのフィールドでJSONを扱う

DjangoのモデルにTextFieldでJSON文字列を保存しておき、APIDjango REST Frameworkで提供する例。

JSONデータを保存して、読み込み時にもJSON形式のまま提供したかった。

restframeworkのシリアライザのFieldクラスを継承し、JSONを扱うフィールドを定義して使う。

コード

app1/models.py:

from django.db import models


class Model1(models.Model):
    class Meta:
        ordering = ['pk']

    data = models.TextField("JSONデータ", null=True, blank=True)

app1/serializers.py:

import json
from rest_framework import serializers

from .models import Model1


class JSONDataField(serializers.Field):
    """JSONテキストのシリアライザ,デシリアライザフィールド
    """
    def to_representation(self, obj):
        if obj:
            return json.loads(obj)
        return None

    def to_internal_value(self, data):
        return json.dumps(data, indent=2)


class Model1Serializer(serializers.ModelSerializer):
    data = JSONDataField()

    class Meta:
        model = Model1
        fields = [
            'id', 'data'
        ]

app1/views.py:

import coreapi
import coreschema
from rest_framework import viewsets
from rest_framework.schemas import AutoSchema

from .serializers import Model1Serializer
from .models import Model1


class Model1Schema(AutoSchema):
    def get_manual_fields(self, path, method):
        # 作成, 更新だけdataフィールドの定義を差し込む
        if method not in ['POST', 'PUT', 'PATCH']:
            return []
        return [
            coreapi.Field(
                "data",
                required=False,
                location="form",
                schema=coreschema.Object()
            )
        ]


class Model1ViewSet(viewsets.ModelViewSet):
    serializer_class = Model1Serializer
    queryset = Model1.objects.all()
    schema = Model1Schema()

REST Frameworkで自動生成されるAPIドキュメントで扱えるように、schema定義を入れています。

app1/urls.py:

from django.urls import path

from rest_framework.documentation import include_docs_urls
from rest_framework.routers import DefaultRouter

from . import views

router = DefaultRouter()
router.register(r'model1', views.Model1ViewSet, base_name='model1')

urlpatterns = [
    path('docs/', include_docs_urls(title='app1 API', public=True)),
] + router.urls

サンプルコード一式

動作例

list: f:id:nullpobug:20180408175330p:plain

create: f:id:nullpobug:20180408175335p:plain