Welcome to restframework-definable-serializer¶
restframework-definable-serializerはYAMLやJSONを用いてdjango-restframeworkのシリアライザークラスを動的に作成することができます。
このライブラリがどのようなものかを知りたい方は restframework-definable-serializerとは を参照してください。
シリアライザーの定義方法については シリアライザーの定義方法 を参照してください。
restframework-definable-serializerを利用することで、少しでもdjangoを利用するエンジニアの手間が減ることを願っています。
インストール方法¶
パッケージのインストール¶
以下のコマンドを実行してインストールしてください:
pip install --upgrade restframework-definable-serializer
警告
もしdjangoとrestframeworkがインストールされていない場合は先にインストールを行ってください:
pip install --upgrade django djangorestframework
INSTALLED_APPSへの追加¶
settings.py中にある INSTALLED_APPS
に codemirror2
と definable_serializer
を追加してください。:
INSTALLED_APPS = (
...
'rest_framework',
'codemirror2',
'definable_serializer',
)
restframework-definable-serializerとは¶
概要¶
django-restframework(以下restframework)のもつ、djangoモデル(以下モデル)からシリアライザーを生成できるモデルシリアライザーはとても強力です。 このおかげでシリアライザー用のコードを書く手間を省くことができます。
しかしモデルシリアライザーから作成したシリアライザーのフィールド変更は、即ちモデルのフィールドを変更と同義です。ひとたび変更が発生すれば、デプロイとマイグレーション作業を行わなければなりません。
しかもマイグレーションを伴うデプロイはひと気がない深夜に行わなければならない場合が多く、これが続くとエンジニアは辟易してしまいます。
例えば名前、年齢、性別だけを扱うアンケートがあるとしましょう。 最初はこれでよかったはずが、顧客からの要望で入力項目がどんどんと増えていき、最後にはかなりの項目数になっていた…。 筆者はこれに近い経験をしたことがあります。これは非常に苦痛でした。
この経験から学べることは 変更が多く発生するシリアライザーはモデルシリアライザーから作成してはいけない ということです。
シリアライザーのフィールドとモデルのフィールドが対になっているが故に、シリアライザーのフィールドが変更されるたびにモデルの変更が発生してしまうのです。
この問題を解決するには 手軽に変更可能な定義からシリアライザーを動的に作る ことです。
definable-serializerは、YAML/JSON(ファイル/文字列)からシリアライザーを作ることができます。
また、シリアライザーの定義を行うためのモデルフィールドや、ユーザーからの入力情報を保存するフィールドなども提供しています。
definable-serializerができること¶
definable-serializerは以下のような機能を備えています。
- YAML/JSON(ファイル/文字列)からdjango-restframeworkで利用可能なシリアライザーを作成する機能を提供します
- adminサイトでシリアライザーを定義するためのモデルフィールドと、記述されたシリアライザーを確認する機能を提供します
- ユーザーからの入力を保存するためのモデルフィールドを提供します
- TemplateHTMLRendererを利用する際に便利なシリアライザーフィールドを提供します(将来的に分離予定です)
文字列やファイルからシリアライザーを作ることもできますが、特にadmin画面でシリアライザーの定義が記述されることを期待しています。

adminサイトで記述したシリアライザー定義と確認
シリアライザー定義の記述例¶
以下にYAMLで定義した簡単なシリアライザーの例を紹介します。
main:
name: EnqueteSerializer
fields:
- name: name
field: CharField
field_kwargs:
required: true
max_length: 100
- name: age
field: IntegerField
field_kwargs:
required: true
- name: gender
field: ChoiceField
field_args:
- - - male
- 男性
- - female
- 女性
field_kwargs:
required: true
上の定義は名前、年齢、性別の3つの入力を持つシリアライザーの例です。 この定義をdefinable-serializerを用いてシリアライザー化すると以下のようになります。
EnqueteSerializer():
name = CharField(max_length=100, required=True)
age = IntegerField(required=True)
gender = ChoiceField([['male', '男性'], ['female', '女性']], required=True)
これをrestframeworkの持つBrowsableAPIRendererで表示すると以下の様になります。

BrowsableAPIRendererで表示した例
YAMLで記述された定義からシリアライザーを作成する¶
実際にYAMLデータから名前を扱う簡単なシリアライザーを作成し、データを入力してバリデーションを行います。
djangoシェルを立ち上げて以下のように打ち込んでみましょう。:
./manage.py shell
djangoのシェルが立ち上がったら以下のコードを実行してみましょう
>>> from definable_serializer.serializers import build_serializer_by_yaml
# 名前だけを扱うシリアライザーのYAML定義
>>> YAML_DEFINE_DATA = """
... main:
... name: YourFirstSerializer
... fields:
... - name: name
... field: CharField
... field_kwargs:
... required: true
... max_length: 100
... """
# シリアライザー化
>>> serializer_class = build_serializer_by_yaml(YAML_DEFINE_DATA)
>>> serializer_class()
FirstSerializer():
name = CharField(max_length=100, required=True)
# バリデーション成功例
>>> serializer = serializer_class(data={"name": "Taro Yamada"})
>>> serializer.is_valid()
>>> serializer.validated_data
OrderedDict([('name', 'Taro Yamada')])
# バリデーションエラー例(空の場合)
>>> serializer = serializer_class(data={"name": ""})
>>> serializer.is_valid()
False
>>> serializer.errors
{'name': ['This field may not be blank.']}
# バリデーションエラー例(100文字を超えていた場合 )
>>> serializer = serializer_class(data={"name": "a" * 101})
>>> serializer.is_valid()
False
>>> serializer.errors
{'name': ['Ensure this field has no more than 100 characters.']}
このように、YAMLで記述された定義からシリアライザーを作成することができました。 次はアンケートを扱うexampleアプリケーションを作成し、definable-serializerをadminサイトへを組み込む例を紹介するとともに、ユーザー側のビューを作成する例も紹介します。
プロジェクトにdefinable-serializerを組み込む¶
definable-serializerの一番の目的は、シリアライザーの入力項目の変更を容易にし、デプロイ作業からエンジニアを守ることです。 故にシリアライザーの定義をファイルに書いては意味がありません。
その問題を解決する一番の方法はWebインターフェイスです。ウェブインターフェイスからシリアライザーの定義を変更することができれば、デプロイを行わずに済みます。 我々はrestframeworkを利用している時点でdjangoを利用しており、djangoはadminサイトにてモデルの追加/変更を簡単に行うことができます。
これを利用してモデルにシリアライザー定義用のフィールドを追加すれば簡単に定義を変更することができます。
definable-serializerではシリアライザー定義を扱うためのモデルフィールドを提供しています。
シリアライザーを定義するためのモデルフィールド¶
definable-serializerではモデルにてYAML/JSONでシリアライザー定義を扱うための DefinableSerializerByYAMLField と DefinableSerializerByJSONField というモデルフィールドを用意しています。
これらのフィールドを利用すると、adminサイト中にCodeMirror2を使ったテキストエリアが現れます。
さらにadminサイト中で記述されたシリアライザーの DefinableSerializerAdmin クラスを提供しています。 ここでは簡単なプロジェクトを作成し、definable-serializerの組み込み例を紹介します。
アンケートシステムを作成してadmin画面でシリアライザーを定義する¶
簡単なアンケート(Survey)を取るためのシステムがあるとします。
しかしこのアンケートシステムの営業担当は顧客に対して寛容な心を持ち、顧客の要望全てに答えようとしてしまいます。 担当のエンジニアは変更のたびにモデルフィールドの追加/削除を求められ、挙句の果てにラベルやヘルプテキストの変更などありとあらゆる要望に答えならなくなったとします。
そんなときこそdefinable-serializerが真価を発揮します。
ここではアンケートをとるためのプロジェクトを作成し、definable-serializeの組み込み方を説明します。
警告
ここではある程度djangoとrestframeworkの扱いを知っている方を対象とします。 また、インストール方法 を読んでいない方は先に読んで準備を整えてください。
この説明中で記載するコードは完全に動作するものではありません。実際に動作するものを確認したい場合は、 完全に動作するサンプルプロジェクトを用意しています。 ので、そちらを参照してください。
exampleプロジェクトとsurveysアプリケーションの作成¶
適当なディレクトリ上で以下のコマンドを実行し、exampleプロジェクトとsurveysアプリケーションを作成します。
$ django-admin.py startproject example_projecet
$ cd ./example_projecet
$ ./manage.py startapp surveys
次に settings.py
中の INSTALLED_APPS
を変更します。
INSTALLED_APPS = (
...
'rest_framework',
'codemirror2',
'definable_serializer',
'surveys',
)
surveysのmodels.pyとadmin.pyの変更¶
models.py
では質問を取り扱う Survey
モデルと、回答データを扱うための Answer
モデルを用意します。
- Surveyモデル
- Surveyモデルには先ほど紹介した DefinableSerializerByYAMLField を利用して質問用のシリアライザー定義を取り扱う
question
フィールドと、 アンケートタイトルを扱うtitle
フィールドを追加します。 - Answerモデル
- Answerモデルには回答対象へリレーションを張るための
survey
フィールドと、 回答データを保持するanswer
フィールドを追加します。
admin.py
ではSurveyモデル、およびAnswerモデルをAdminサイトで確認できるように変更を行います。
models.pyを変更する¶
surveys/models.py を変更します。
Surveyモデルは、models.Model
ではなく AbstractDefinitiveSerializerModel を継承している点に注意してください。
# surveys/models.py
from django.db import models
from django.conf import settings
from definable_serializer.models import (
DefinableSerializerByYAMLField,
AbstractDefinitiveSerializerModel,
)
from definable_serializer.models.compat import YAMLField
class Survey(AbstractDefinitiveSerializerModel):
title = models.CharField(
null=False,
blank=False,
max_length=300,
)
# YAMLで定義されたシリアライザーを扱うフィールド
question = DefinableSerializerByYAMLField()
def __str__(self):
return self.title
class Answer(models.Model):
survey = models.ForeignKey("Survey")
respondent = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
answer = YAMLField(
null=False,
blank=False,
default={},
verbose_name="answer data",
help_text="answer data"
)
class Meta:
unique_together = ("survey", "respondent",)
admin.pyを変更する¶
admin画面にsurveyモデル,及びAnswerモデルを確認/変更するページを表示するため、 surveys/admin.py を変更します。 SurveyAdminクラスは、admin.ModelAdminではなく、 DefinableSerializerAdmin を継承している点に注意してください
# surveys/admin.py
from django.contrib import admin
from definable_serializer.admin import DefinableSerializerAdmin
from surveys import models as surveys_models
@admin.register(surveys_models.Survey)
class SurveyAdmin(DefinableSerializerAdmin):
list_display = ("id", "title",)
list_display_links = ("id", "title",)
@admin.register(surveys_models.Answer)
class AnswerAdmin(admin.ModelAdmin):
list_display = ("id", "survey", "respondent",)
list_display_links = ("id", "survey",)
作業が完了するとadminサイトにSurveyモデルとAnsweモデルの変更を行うページが追加されます。
質問用のシリアライザー定義を記述する¶
adminサイトを確認するために開発用サーバーを起動します。初回起動のため、マイグレーション作業及びadminアカウントを作成した後に開発用サーバーを起動します。
$ ./manage.py makemigrations
...
$ ./manage.py migrate
...
$ ./manage.py createsuperuser
Username (leave blank to use 'your-name'): admin
Email address: admin@example.com
Password: <password>
Password (again): <password>
Superuser created successfully.
$ ./manage.py runserver 0.0.0.0:8000
Django version 1.11.6, using settings 'example_project.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.
起動したら http://localhost:8000/admin/surveys/survey/add/survey をブラウザーで開いてSurveyモデルのadmin画面にアクセスしましょう。
タイトルとYAMLで記述されたシリアライザー定義を入力します。ここでは名前、年齢、性別の3つを扱う簡単なシリアライザーを定義します。 以下のYAMLデータをquestionのフィールドにコピー&ペーストしてください。(タイトルは適当で構いません)
main:
name: EnqueteSerializer
fields:
- name: name
field: CharField
field_kwargs:
required: true
max_length: 100
- name: age
field: IntegerField
field_kwargs:
required: true
- name: gender
field: ChoiceField
field_args:
- - - male
- 男性
- - female
- 女性
field_kwargs:
required: true
入力が完了したら、[保存して編集を続ける]ボタンを押します。すると、編集画面の上部に定義したシリアライザーのクラス情報が表示されます。

保存後に問題がなければシリアライザークラスの情報がページ上部に表示されます。
また、定義されたシリアライザーをrestframeworkのもつBrowsable APIのページを使って確認することもできます。
タイトルラインにある [Show Restframework Browsable Page] のリンクをクリックすると、 Browsable APIのページが開き、定義したシリアライザーの入力テストを行うことができます。

Browsable APIで確認した例
シリアライザーを確認できたところで、次は定義を変更してみましょう。例として紹介文用のフィールド、 introduction
を追加します。
main:
name: EnqueteSerializer
fields:
...
- name: introduction
field: definable_serializer.extra_fields.TextField
field_args:
required: true
placeholder: Hello!
追加が完了したらモデルを保存して、再度 Browsable APIのページでシリアライザーの状態を確認してみましょう。 問題がなければ、テキストエリアが追加されます。

定義が正しければテキストエリアが追加されます
次はユーザーがアンケートの回答および入力内容の変更/確認を行うビューを作成します。
ユーザーからの回答を受け付けるビューの作成¶
restframeworkを利用する場合、REST API経由でやり取りをするケースが多いと思いますが、
ここではrestframeworkが持つ TemplateHTMLRenderer
も同時にサポートしてユーザーの回答用ビューを作成します。
このビューにおいて問題になるのが、Surveyモデルオブジェクト中のシリアライザー定義からシリアライザークラスを取り出す方法と、 POSTされた回答内容をどのように保存するかという点です。
definable-serializerではこれらの問題を解決するための方法を提供しています。
モデルからシリアライザークラスを取り出す方法¶
シリアライザー定義用フィールドを持つモデルオブジェクトからシリアライザークラスを取り出すのはさほど難しくありません。
先ほど定義したSurveyモデルは AbstractDefinitiveSerializerModel を継承しており、
シリアライザークラスを取り出すためのメソッドである get_question_serializer_class
がモデルオブジェクトに自動で追加されるからです。
例として先ほど作成したSurveyモデルオブジェクトから question
フィールドに記述したシリアライザー定義の
シリアライザークラスを取り出します。
>>> from surveys import models as surveys_models
>>> survey_obj = surveys_models.Survey.objects.get(pk=1)
>>> question_serializer_class = survey_obj.get_question_serializer_class()
>>> question_serializer = question_serializer_class()
>>> print(question_serializer)
EnqueteSerializer():
name = CharField(max_length=100, required=True)
age = IntegerField(required=True)
gender = ChoiceField([['male', '男性'], ['female', '女性']], required=True)
introduction = TextField(placeholder='Hello!', required=True)
ヒント
例えば foobar
というモデルフィールドが
DefinableSerializerByYAMLField または DefinableSerializerByJSONField のどちらかを利用していたら、
get_foobar_serializer_class
というメソッド名でシリアライザークラスを取り出すことができます。
(ただし、モデルが AbstractDefinitiveSerializerModel を継承している場合のみに限ります)
入力された内容を保存する方法¶
definable-serializerでは、シリアライザーのフィールドとモデルのフィールドを対にしないという理念のもと作られています。 そのためシリアライザーに渡されたユーザーからの入力内容は、モデルの単一のフィールドにJSON/YAML/Pickle等にシリアライズ(直列化)して保存する必要があります。
definable-serializerでは、ユーザーからの入力を保存するために JSONField と YAMLField を用意しています。 先ほど作成したmodels.py中のAnswerモデルのanswerフィールドは YAMLField を利用しています。
以下にAnswerモデルに追加したanswerフィールドにアンケートの内容を保存するためのコード例を示します。
# シリアライザークラスを作成してデータを渡し、バリデーションを行う
>>> from surveys import models as surveys_models
>>> survey_obj = surveys_models.Survey.objects.get(pk=1)
>>> question_serializer_class = survey_obj.get_question_serializer_class()
>>> question_serializer = question_serializer_class(data={
... "name": "John Smith",
... "age": 20,
... "gender": "male",
... "introduction": "Hi!"
... })
>>> question_serializer.is_valid()
True
>>> from django.contrib.auth import get_user_model
>>> admin_user = get_user_model().objects.get(pk=1)
>>> print(admin_user)
admin
>>> answer_obj = surveys_models.Answer.objects.create(
... survey=survey_obj,
... respondent=admin_user,
... answer=question_serializer.validated_data
... )
>>> answer_obj.answer
odict_values(['John Smith', 20, 'male', 'Hi!'])
実際に入れたデータをadminサイトで確認してみましょう。YAML形式で保存されていることが確認できます。

!!Ordered Mapping で保存されていることが確認できます。
ヒント
例としてYAMLFieldを用いてバリデーション後の結果を保存しましたが、モデルフィールドさえ提供されていれば、色々な形式で保存することが出来ます。 詳しくは ユーザーからの入力データを保存するモデルフィールド を参照してください
警告
保存したJSONデータを検索の対象としたい場合はdjangoの提供する
django.contrib.postgres.fields.JSONField
を利用することを強くおすすめします。
ただし、そのままではいくつかの問題があります。詳しくは JSONFieldでデータを扱う場合の問題点 を御覧ください。
ユーザー回答用ビューの作成例¶
上の内容を踏まえて回答用ビューの作成例を示します。
警告
下記に示すコードは作成例です。 urls.pyへの登録、テンプレートの用意、登録後のリダイレクト先が存在しない等の問題により、このままでは正しく動作しません。 ここではそれらが完全に揃っていることにして説明を続けます。
実際に動作するものを確認したい場合は 完全に動作するExampleプロジェクトを用意しています
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.renderers import TemplateHTMLRenderer, JSONRenderer
from rest_framework.exceptions import MethodNotAllowed, NotFound
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import (
SessionAuthentication, TokenAuthentication
)
from . import models as surveys_models
class Answer(APIView):
"""
Answer API
"""
allowed_methods = ("GET", "POST", "OPTIONS",)
renderer_classes = (TemplateHTMLRenderer, JSONRenderer,)
authentication_classes = (SessionAuthentication, TokenAuthentication,)
permission_classes = (IsAuthenticated,)
template_name = 'answer.html'
def _get_previous_answer(self, survey):
"""
過去の回答データを取得します。存在しない場合はNoneを返します
"""
previous_answer = None
try:
previous_answer = surveys_models.Answer.objects.get(
respondent=self.request.user, survey=survey)
except surveys_models.Answer.DoesNotExist:
pass
return previous_answer
def initial(self, request, *args, **kwargs):
super().initial(request, *args, **kwargs)
survey = get_object_or_404(
surveys_models.Survey, pk=kwargs.get('survey_pk'))
self.previous_answer = self._get_previous_answer(survey)
self.survey = getattr(self.previous_answer, "survey", None) or survey
def get_serializer(self, *args, **kwargs):
"""
質問用のシリアライザークラスを返します
"""
return self.survey.get_question_serializer_class()(*args, **kwargs)
def get(self, request, survey_pk, format=None):
"""
Request HeaderのAcceptが "application/json" の場合はJSONRendererで
過去の入力データを返します。回答がない場合は404を返します。
Request HeaderのAcceptが "application/json" 以外の場合、質問の入力画面を表示します。
ユーザーが過去に同じ質問に回答していた場合、回答データを復元して表示します。
"""
response = None
serializer = self.get_serializer()
if self.previous_answer:
serializer = self.get_serializer(data=self.previous_answer.answer)
serializer.is_valid()
if isinstance(self.request.accepted_renderer, TemplateHTMLRenderer):
response = Response(
{'serializer': serializer, 'survey': self.survey})
else:
if not self.previous_answer:
raise NotFound()
response = Response(serializer.data)
return response
def post(self, request, survey_pk):
"""
回答データの投稿を受け付けます。入力内容に不備があった場合はそれぞれのレンダラーでエラーレスポンスを返します。
回答データに問題がなく、TemplateHTMLRendererを利用する場合はトップ画面にリダイレクトします。
JSONRendererの場合は成功レスポンスを返します。
また、過去に投稿がない場合は新しくAnswerオブジェクトを作成し、投稿があった場合はAnswerオブジェクトを更新します。
"""
response = None
serializer = self.get_serializer(data=self.request.data)
if isinstance(self.request.accepted_renderer, TemplateHTMLRenderer):
response = HttpResponseRedirect("/")
if not serializer.is_valid():
response = Response(
{'serializer': serializer, 'survey': self.survey})
else:
messages.add_message(
request, messages.SUCCESS, 'Thank you for posting! 💖')
else:
serializer.is_valid(raise_exception=True)
response = Response(serializer.data)
if serializer.is_valid():
if self.previous_answer:
self.previous_answer.answer = serializer.validated_data
self.previous_answer.save()
else:
surveys_models.Answer.objects.create(
survey=self.survey,
respondent=request.user,
answer=serializer.validated_data
)
return response
def options(self, request, *args, **kwargs):
"""
APIスキーマやその他のリソース情報を返します。
ただし、Request HeaderのAcceptが "text/html"の場合は 405(Method Not Allowed)を返します。
"""
if request.accepted_media_type == TemplateHTMLRenderer.media_type:
raise MethodNotAllowed(
"It can not be used except when "
"it is content-type: application/json."
)
return super().options(request, *args, **kwargs)
回答用ビューのアクセス例¶
Postmanを用いてREST API経由のレスポンスを得た場合¶
Chromeの機能拡張であるPostman を用いてREST API経由で回答を行った場合の例を示します。

警告
REST API経由でアクセスを行う場合は、Headersタブにて Accept
, Authorization
, Content-Type
の3つを適切に指定してください。

Postmanを用いてOPTIONSメソッドでレスポンスを得た場合¶
OPTIONS
メソッドでアクセスするとREST APIの詳細情報及びPOST時のJSONスキーマが表示されます。
以下にレスポンス例を示します。
{
"name": "Answer",
"description": "Answer API",
"renders": [
"text/html",
"application/json"
],
"parses": [
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
],
"actions": {
"POST": {
"name": {
"type": "string",
"required": true,
"read_only": false,
"label": "Name",
"max_length": 100
},
"age": {
"type": "integer",
"required": true,
"read_only": false,
"label": "Age"
},
"gender": {
"type": "choice",
"required": true,
"read_only": false,
"label": "Gender",
"choices": [
{
"value": "male",
"display_name": "男性"
},
{
"value": "female",
"display_name": "女性"
}
]
},
"introduction": {
"type": "string",
"required": true,
"read_only": false,
"label": "Introduction"
}
}
}
}
シリアライザーの定義方法¶
restframeworkのシリアライザーは、システムからの出力とユーザーからの入力を適切に処理するために存在します。 これはdjangoのフォームと変わりません。しかし、djangoの提供するフォームはネストした複雑なデータの取り扱いには向いていません。
それに比べ、restframeworkのシリアライザーはネストされたデータも上手に扱うことができるため、djangoのformよりもパワフルです。 djangoのフォームも、formsets等を利用して複数のフォームを並べることもできますが、UI/UXの観点からみると絶望的な状況になるでしょう。
ここではdefinable-serializerを用いて単純なシリアライザーと、ネストされたシリアライザーをYAMLで定義する方法を解説します。
またシリアライザーが持つフィールドの記述方法と、validateメソッドを含むシリアライザーの記述方法についても解説します。
単純なシリアライザーとネストされたシリアライザー¶
単純なシリアライザー¶
単純なシリアライザーとは、フィールド中に別のシリアライザーをネストしていないものを指します。 例えばファーストネームとラストネームのみを扱うような構造のシリアライザーの場合は以下のように記述します。
main:
name: NameEntry
fields:
- name: first_name
field: CharField
field_kwargs:
required: true
max_length: 100
- name: last_name
field: CharField
field_kwargs:
required: true
max_length: 100
この定義をシリアライザークラス化すると以下のようになります。
NameEntry():
first_name = CharField(max_length=100, required=True)
last_name = CharField(max_length=100, required=True)
このシリアライザーに渡せるデータは以下のような形式になります。
{
"first_name": "John",
"last_name": "Smith",
}
ネストされたシリアライザー¶
ネストされたシリアライザーとは、シリアライザー中に他のシリアライザーを含むものを指します。 例えばSNSのようにグループに人を紐付けるような構造のシリアライザーがそれにあたります。
以下の記述例を示します。
main:
name: Group
fields:
- name: group_name
field: CharField
field_kwargs:
label: Group name
required: true
- name: persons
field: Person
field_kwargs:
many: true
depending_serializers:
- name: Person
fields:
- name: first_name
field: CharField
field_kwargs:
required: true
- name: last_name
field: CharField
field_kwargs:
required: true
この定義をシリアライザークラス化すると以下のようになります。
Group():
group_name = CharField(label='Group name', required=True)
persons = Person(many=True):
first_name = CharField(required=True)
last_name = CharField(required=True)
このシリアライザーに渡せるデータは以下のような形式になります(Persons部分がネストされたシリアライザーになります)。
{
"group_name": "My dearest friends",
"persons": [
{"first_name": "John", "last_name": "Smith"},
{"first_name": "Taro", "last_name": "Yamada"}
]
}
ここで注目するべきは、定義中の depending_serializers
の項目です。
この項目は、mainのシリアライザーを作成する前に予め作成されるシリアライザークラスのリストになります。
depending_serializers
中で先にシリアライザーの定義がされていれば、後に記述されるシリアライザーはそれらを利用することができます。
main:
name: YourFavorite
fields:
- name: foods_and_animal
field: FoodsAndAnimals
field_kwargs:
many: true
depending_serializers:
- name: Animal
fields:
- name: name
field: CharField
- name: Food
fields:
- name: name
field: CharField
- name: FoodsAndAnimals
fields:
- name: animals
field: Animal # 上で定義されているAnimalを利用しています
field_kwargs:
many: true
- name: foods # 上で定義されているFoodを利用しています
field: Food
field_kwargs:
many: true
この定義をシリアライザークラス化すると以下のようになります。
YourFavorite():
foods_and_animal = FoodsAndAnimals(many=True):
animals = Animal(many=True):
name = CharField()
foods = Food(many=True):
name = CharField()
シリアライザーフィールドの記述方法¶
シリアライザーにはフィールドが必要です。フィールドを記述するにはフィールド名とフィールドタイプを必ず指定します。 任意でフィールドの引数、名前付き引数を指定することができます。以下にフィールドの記述例を示します。
- name: gender # フィールド名
field: ChoiceField # フィールドタイプ(フィールドクラス名)
field_args: # フィールドタイプの引数(list)
- - - male
- 男性
- - female
- 女性
field_kwargs: # フィールドタイプの名前付き引数(dict)
required: true
label: 性別を入力してください
上記の定義は以下のPythonコードと同義になります。
>>> from rest_framework import serializers
>>> gender = serializers.ChoiceField(
... [["male", "男性"], ["female", "女性"]],
... required=True,
... label="性別を入力してください"
... )
>>> gender
ChoiceField([['male', '男性'], ['female', '女性']], label='性別を入力してください', required=True)
restframeworkが提供するシリアライザーフィールドの利用¶
警告
definable-serializerでは DictField
, ListField
及び SerializerMethodField
以外のシリアライザーフィールドが利用可能です。(これらのフィールドは将来的にサポートされる予定です)
definable-serializerではrestframeworkが提供するほとんどのシリアライザーフィールドを利用することができます。 シリアライザーフィールドの一覧については restframeworkのシリアライザーフィールドのページを参照してください
restframeworkが提供するシリアライザーフィールドを利用する場合はクラス名だけを指定します。
- name: my_checkbox # フィールド名
field: BooleanField # フィールドタイプ
- name: my_char # フィールド名
field: CharField # フィールドタイプ
- name: my_regex_field # フィールド名
field: RegexField # フィールドタイプ
field_args:
- a-zA-Z0-9
サードパーティパッケージが提供するシリアライザーフィールドの利用¶
definable-serializerではサードパーティパッケージ、つまりrestframework以外が提供するシリアライザーフィールドも利用可能です。
利用する場合は、各フィールド定義の field
項目 に <パッケージ名>.<モジュール名>.<クラス名>
の形式で指定します。
main:
name: AgreementSerailizer
fields:
- name: agreement
field: definable_serializer.extra_fields.CheckRequiredField # サードパッケージが利用するシリアライザーフィールド
field_kwargs:
initial: false
この定義をシリアライザークラス化すると以下のようになります。
IncludeExtraSerializerField():
agreement = CheckRequiredField()
ヒント
definable-serializerでは TemplateHTMLRendererに向けて、いくつかのシリアライザーフィールドを提供しています。 提供するシリアライザーフィールドクラス を御覧ください
フィールドの国際化¶
0.1.12で登場しました
djangoは国際化機構を提供しているため、システム全体の翻訳を行うことが可能です。
しかしこの機構はgettextを利用するため、コンパイルされた翻訳ファイルをサーバーにデプロイする必要があります。 definable-serializerの目的はデプロイの手間を減らすことなので、残念ながらこの機構を利用するわけにはいきません。
この問題を回避する方法として、localeと翻訳テキストを対にした辞書を翻訳が必要なフィールドに指定し、ユーザーのlocaleを元に翻訳テキストに切り替える方法を利用します。 翻訳のテキストを指定出来るフィールドは引数中の以下の項目になります。
choices
引数label
キーワード引数help_text
キーワード引数initial
キーワード引数style
キーワード引数中のplaceholder
キーワード引数
指定された翻訳テキストは request.LANGUAGE_CODE
情報を元に取り出され、シリアライザークラスをオブジェクト化する際に適応されます。
また、locale情報に対応するキーが存在しない場合は default
キーにフォールバックするため、必ず指定する必要があります。
警告
locale情報の取得にはdjangoが提供するLocaleMiddlewareを利用する必要があります。
settings.pyの MIDDLEWARE
に django.middleware.locale.LocaleMiddleware
を追加してください。
詳しくは Translation を参照してください
以下に翻訳テキストを含めたシリアライザーの定義例を示します。
main:
name: I18NTest
fields:
- name: animal_field
field: ChoiceField
field_args:
- - -
- default: '------- Please choice me -------'
ja: '------- 選択してください -------'
- - dog
- default: Dog 🐶
ja: イヌ 🐶
- - cat
- default: Cat 😺
ja: ネコ 😺
- - rabbit
- default: Rabbit 🐰
ja: ウサギ 🐰
field_kwargs:
help_text:
default: Please select one of your favorite animal 😁
ja: 好きな動物を選んでね 😁
label:
default: Favorite Animal
ja: 好きな動物
- name: introduction_field
field: CharField
field_kwargs:
help_text:
default: Please input your introduction.
ja: 自己紹介を入力してください
label:
default: introduction
ja: 自己紹介
style:
base_templaet: textarea.html
rows: 5
placeholder:
default: Lorem ipsum dolor sit amet, consectetur...
ja: あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ...
Browsable APIの画面を確認すると以下の様に翻訳が適応された状態で表示されます。

ユーザーのlocaleがjaの場合

ユーザーのlocaleが存在しなかった場合
ヒント
国際化が正しく行われたかを確認するためには、ブラウザーの Accept-Language
を変更する必要があります。
もし、あなたがChromeを利用している場合は Quick Language Switcher の利用をおすすめします。
validateメソッドを含んだシリアライザー¶
シリアライザーやシリアライザーフィールドにはカスタムされたvalidate用のメソッドを必要とする場合があります。 これらはPythonのコードを記述する必要があります。
definable-serializerではフィールド、シリアライザーともにvalidateメソッドを記述することできます。
警告
シリアライザーの定義中にvalidateメソッドを記述できることは便利な半面、危険を伴います。 例えば、シリアライザーの定義を一般のユーザーに記述させたい要望があったとします。 もし、一般ユーザーがvalidateメソッド中にシンタックスエラーを含む内容を記述してしまうと、エラーでクラッシュしてしまいます。
また悪意のあるコードを書かれてしまうと、素直にそれが実行されてしまいます!
もし、一般ユーザーにシリアライザーを記述させたい場合は、 allow_validate_method
オプションを False
にして
validateメソッドの記述を不許可にすることを強く推奨します。 詳しくは以下を御覧ください。
フィールドのvalidateメソッド¶
フィールドにvalidateメソッドを追加するには以下のように記述します。
main:
name: FieldValidationTestSerializer
fields:
- name: test_field_one
field: CharField
field_kwargs:
required: true
# Field validation method
validate_method: |
def validate_method(self, value):
from rest_framework import serializers
if value != "correct_data":
raise serializers.ValidationError("Please input 'correct_data'")
return value
以下にバリデーションの結果を示します。
>>> from definable_serializer.serializers import build_serializer_by_yaml
>>> YAML_DEFINE_DATA = """<< FieldValidationTestSerializer YAML DATA >>"""
>>> serializer_class = build_serializer_by_yaml(YAML_DEFINE_DATA)
>>> serializer = serializer_class(data={"test_field_one": "test"})
# フィールドバリデーションエラー例
>>> serializer.is_valid()
False
>>> serializer.errors
ReturnDict([('test_field_one', ["Please input 'correct_data'"])])
# フィールドバリデーション成功例
>>> serializer = serializer_class(data={"test_field_one": "correct_data"})
>>> serializer.is_valid()
True
シリアライザーのvalidateメソッド¶
パスワードの確認フィールドのように、他のフィールドの入力データを利用する場合はシリアライザーのvalidateメソッドを記述する必要があります。
そのような必要がある場合は以下のように記述します。
main:
name: PasswordTestSerializer
fields:
- name: password
field: CharField
field_kwargs:
required: true
- name: password_confirm
field: CharField
field_kwargs:
required: true
# Serializer validation method
serializer_validate_method: |-
def validate_method(self, data):
from rest_framework import serializers
if data["password"] != data["password_confirm"]:
raise serializers.ValidationError({
"password_confirm": "The two password fields didn't match.'."
})
return data
以下にバリデーションの結果を示します。
>>> from definable_serializer.serializers import build_serializer_by_yaml
>>> YAML_DEFINE_DATA = """<< PasswordTestSerializer YAML DATA >>"""
>>> serializer_class = build_serializer_by_yaml(YAML_DEFINE_DATA)
# バリデーションエラー例
>>> serializer = serializer_class(
... data={"password": "new_password", "password_confirm": "foobar"})
...
>>> serializer.is_valid()
False
>>> serializer.errors
ReturnDict([('password_confirm',
["The two password fields didn't match."])])
# バリデーション成功例
>>> serializer = serializer_class(
... data={"password": "new_password", "password_confirm": "new_password"})
...
>>> serializer.is_valid()
True
Validatorクラスの利用¶
0.1.11で登場しました。
validateメソッドはPythonのコードを記述して入力内容のバリデーションを行うことができるため、非常に強力です。 その反面、恐ろしい事態を引き起こす要因でもあります。 また、validateメソッドは使いまわすことができないため、同じルーチンでバリデーションを行いたいフィールドが複数あると、validateメソッドを何度も記述するハメになります。 これは苦行に他なりません。
この問題を解決するには Validator クラスを利用します。
Validatorクラスを利用する場合は、各フィールドの validators
のリスト中に辞書形式で以下の様に記述します(複数指定可能)。
validators:
- validator: <パッケージ名>.<モジュール名>.<クラス名>
args:
...
kwargs:
...
- validator: <パッケージ名>.<モジュール名>.<クラス名>
args:
...
kwargs:
...
利用するValidatorクラスは <パッケージ名>.<モジュール名>.<クラス名>
の形式で指定します。
引数が必要な場合は args
または kwargs
を渡すことができます。
以下にテスト用のValidatorクラスを利用した記述例とバリデーション後の結果を示します。
main:
name: UsingValidator
fields:
- name: hello_only_field
field: CharField
field_kwargs:
style:
placeholder: "..."
validators:
- validator: definable_serializer.tests.test_serializers.CorrectDataValidator
args:
- hello
- name: goodbye_only_field
field: CharField
field_kwargs:
style:
placeholder: "..."
validators:
- validator: definable_serializer.tests.test_serializers.CorrectDataValidator
args:
- goodbye

Validatorクラスを利用した例
警告
CorrectDataValidatorクラスはテスト用です。テスト以外では利用しないように注意してください。
カスタムされた基底クラスまたはミックスインクラスの指定¶
0.1.14で登場しました。
Python のクラス機構はオブジェクト指向プログラミングの標準的な機能を全て提供しています。 クラスの継承メカニズムは、複数の基底クラスを持つことができ、派生クラスで基底クラスの任意のメソッドをオーバライドすることができます。
definable_serializerでは、作成されるシリアライザークラスに対して基底クラス、もしくはミックスインクラスを指定するいくつかの方法を提供しています。
django.settingsで指定する¶
以下のように基底クラス、またはミックスインクラスを文字列で複数指定することができます。
DEFINABLE_SERIALIZER_SETTINGS = {
"BASE_CLASSES": [
"foo.bar.MixinClassOne",
"foo.bar.MixinClassTwo",
...
]
}
以降、definable_serializerで作成されるクラスは、上記で指定したクラスを継承します。
build_serializerの引数を指定する¶
build_serializer関数を通してシリアライザークラスを作成する場合は、base_classes
引数に基底クラス、またはミックスインクラスを指定します。
>>> from definable_serializer.serializers import build_serializer
>>> class MixinClassOne:
... it_is_one = True
...
>>> class MixinClassTwo:
... it_is_two = True
...
>>> serializer_class = build_serializer(
... defn_data
... base_classes=[
... MixinClassOne,
... MixinClassTwo,
... ],
... )
...
>>> serializer = serializer_class()
>>> getattr(serializer, "it_is_one")
True
>>> getattr(serializer, "it_is_two")
True
base_classesの引数は以下のユーティリティ関数でも指定することができます
提供するモデルフィールドクラス¶
definable-serializerではシリアライザーを記述するためのフィールドと、 ユーザーからの入力データを保存するためのフィールドを提供しています。
シリアライザーの定義を保存するモデルフィールド¶
definable-serializerでは、JSON/YAMLで記述された文字列及びファイルからシリアライザーを作ることができます。 特にadminサイトでシリアライザーの定義を記述することで、デプロイの手間を無くすのが目的です。 adminサイト上でテキストデータの編集を行うのは難しい話ではないものの、YAMLやJSONをコードハイライト無しで記述するのはちょっとした苦行です。
この問題を解決するために、CodeMirror2ウィジェットを組み込んだシリアライザー定義用のフィールドを用意しています。
DefinableSerializerByYAMLField¶
-
class
DefinableSerializerByYAMLField
(*args, allow_validate_method=True, **kwargs)¶
DefinableSerializerByYAMLFieldは https://github.com/datadesk/django-yamlfield が 提供するYAMLFieldをラップし、CodeMirror2ウィジェットの利用及び非ASCII文字が正しく表示できるようにカスタマイズしています。
allow_validate_method
が False
の場合、シリアライザーの定義中に validate_method
が記述されていると ValidationError
が発生します。
その他のオプションについては https://github.com/datadesk/django-yamlfield を参照してください。
以下に記述例を示します。
class Survey(models.Model):
..
question = DefinableSerializerByYAMLField()

DefinableSerializerByJSONField¶
-
class
DefinableSerializerByJSONField
(*args, allow_validate_method=True, **kwargs)¶
DefinableSerializerByJSONFieldは https://github.com/dmkoch/django-jsonfield が 提供するJSONFieldをラップし、CodeMirror2ウィジェットの利用及び非ASCII文字が正しく表示できるようにカスタマイズしています。
allow_validate_method
が False
の場合、シリアライザーの定義中に validate_method
が記述されていると ValidationError
が発生します。
その他のオプションについては https://github.com/dmkoch/django-jsonfield を参照してください。
以下に記述例を示します。
class Survey(models.Model):
..
question = DefinableSerializerByJSONField()

ユーザーからの入力データを保存するモデルフィールド¶
入力された内容を保存する方法 でも取り上げたように、モデルに結びつかないシリアライザーの持つユーザーからの入力データを永続的に保存するには、 保存を担うモデルクラスのフィールドにシリアライズ(直列化)された状態でデータを保存します。
ようはPythonのネイティブなデータをテキストやバイナリに変換してデータベースのカラム、即ちモデルフィールドに保存できればどんな形でも構いません。
definable-serializerではユーザーからの入力を保存するために2つのモデルフィールドを用意しています。
JSONField¶
-
class
JSONField
(*args, **kwargs)¶
JSONは人気の高いシリアライズの形式です。しかし、Pythonに付属するjsonモジュールはPythonのネイティブなデータ型である set型
をシリアライズすることができません。
またensure_asciiの設定を行わないと非ASCII文字を "\uXXXX" で表してしまうため、入力情報を確認する際に見苦しい状態になります。
definable-serializerでは、 jsonfield が提供するJSONFieldをラップし、 これらの問題を解消するコンパチビリティクラスを提供しています。
以下に使用例を示します。
from definable_serializer.models.compat import JSONField as CompatJSONField
class Answer(models.Model):
..
answer = CompatJSONField(
verbose_name="answer data",
help_text="answer data"
)
このモデルフィールドを使うとadmin画面で以下のように表示されます。

非ASCII文字列が正しく表示されます
YAMLField¶
-
class
YAMLField
(*args, **kwargs)¶
YAMLはJSONと同様、テキストでデータをシリアライズします。記号が少なくインデントでデータ構造を表すため、Pythonのコードのように可読性に優れます。
definable-serializerでは、 django-yamlfield (https://github.com/datadesk/django-yamlfield) が提供するYAMLFieldをラップし、非ASCII文字が正しく表示されるコンパチビリティクラスを提供しています。
以下に使用例を示します。
from definable_serializer.models.compat import YAMLField as CompatYAMLField
class Answer(models.Model):
..
answer = CompatYAMLField(
verbose_name="answer data",
help_text="answer data"
)
提供するモデルクラス¶
definable-serializerでは、 シリアライザーの定義を保存するモデルフィールド で紹介したシリアライザーの定義を記述するためのモデルフィールドを提供しています。
このシリアライザー定義用フィールドからシリアライザークラスを取り出すには、2つの方法があります。
1つは定義用フィールドからYAML/JSON文字列を取り出し build_serializer_by_yaml関数 や build_serializer_by_json関数 に渡す方法です。
もう1つは AbstractDefinitiveSerializerModel
を継承したモデルクラスを用意する方法です。
ここでは、AbstractDefinitiveSerializerModel
が提供する機能について説明します。
AbstractDefinitiveSerializerModel¶
-
class
AbstractDefinitiveSerializerModel
(*args, **kwargs)¶
このモデルクラスは django.db.models.Model
を親クラスとしており、
__init__
メソッドの中で DefinableSerializerByYAMLField および
DefinableSerializerByJSONField を利用しているフィールドを自動で探し、
get_<フィールド名>_serializer_class
というメソッドをモデルオブジェクトに付与します。
モデルクラスは複数のシリアライザー定義用のフィールドを持つことも可能です。
以下のようなモデルクラスを定義した場合、 get_foo_serializer_class
と get_bar_serializer_class
という2つのメソッドが自動でモデルオブジェクトに付与されます。
from definable_serializer.models import (
DefinableSerializerByYAMLField,
DefinableSerializerByJSONField,
AbstractDefinitiveSerializerModel,
)
class MyModel(AbstractDefinitiveSerializerModel):
...
foo = DefinableSerializerByYAMLField()
bar = DefinableSerializerByJSONField()
>>> my_model = MyModel.objects.get(pk=1)
>>> my_model.get_foo_serializer_class
function
>>> my_model.get_bar_serializer_class
function
>>> my_model.get_foo_serializer_class()
NameEntry():
first_name = CharField(max_length=100, required=True)
last_name = CharField(max_length=100, required=True)
>>> my_model.get_bar_serializer_class()
Group():
group_name = CharField(label='Group name', required=True)
persons = Person(many=True):
first_name = CharField(required=True)
last_name = CharField(required=True)
提供するモデルアドミンクラス¶
JSONやYAMLで定義したシリアライザーは、実際にシリアライザークラスにしないと確認することができません。
YAMLで記述された定義からシリアライザーを作成する で説明した方法で確認することもできますが、入力や見栄えのテストを行うのには不十分です。
そのため、definable-serializerでは django.contrib.admin.ModelAdmin
を拡張した DefinableSerializerAdmin
クラスを提供しています。
DefinableSerializerAdmin¶
-
class
DefinableSerializerAdmin
¶
DefinableSerializerAdminクラスでは以下の機能を提供します。
- 編集画面上部に定義されたシリアライザークラスの情報を表示する機能
- restframeworkのもつBrowsable APIページを利用して定義されたシリアライザーを表示する機能
特にBrowsable APIページでは定義したシリアライザーの入力テストを行うことができるため、
非常に有用です。組み込み方も非常に簡単で、通常は admin.ModelAdmin
を利用するところを
DefinableSerializerAdmin
に入れ替えるだけです。
以下に使用例を示します。
from django.contrib import admin
from definable_serializer.admin import DefinableSerializerAdmin
from . import models as surveys_models
@admin.register(surveys_models.Survey)
class SurveyAdmin(DefinableSerializerAdmin):
list_display = (
"id",
"title",
)
list_display_links = (
"id",
"title",
)
正しく組み込まれるとadmin画面が以下のようになります。

画面上部にシリアライアーの定義が表示されます。
提供するシリアライザーフィールドクラス¶
Zen Of Pythonの 暗示するより明示するほうがいい という観点からdefinable-serializerでは TemplateHTMLRenderer のためにいくつかのフィールドを提供しています。
警告
これらのフィールドは将来的に別パッケージとして提供される可能性があります。
CheckRequiredField¶
-
class
CheckRequiredField
(*args, **kwargs)¶
必ずOnをしなければならないチェックボックスを提供します。ユーザーの意思確認などを行いたい場合に利用します。
このクラスは restframeworkの BooleanField
を継承してつくられています。
オプションについては BooleanField を参照してください。
シリアライザー定義のfieldには definable_serializer.extra_fields.CheckRequiredField
を指定します。
main:
name: Agreement
fields:
- name: agreement
field: definable_serializer.extra_fields.CheckRequiredField
MultipleCheckboxField¶
-
class
MultipleCheckboxField
(choices, *args, required=False, inline=False, **kwargs)¶
複数のチェックボックスによる選択肢を表示するフィールドを提供します。
fieldには definable_serializer.extra_fields.MultipleCheckboxField
を指定します。
required
をtrue
にすると必須選択になります。inline
をtrue
にするとチェックボックスが横並びに表示されます。
このクラスはrestframeworkの MultipleChoiceField
を継承してつくられています。その他のオプションについては
MultipleChoiceField を参照してください。
main:
name: YourFavoriteAnimal
fields:
- name: animal_choice_field
field: definable_serializer.extra_fields.MultipleCheckboxField
field_args:
- - - dog
- 🐶Dog
- - cat
- 😺Cat
- - rabbit
- 🐰Rabbit
field_kwargs:
inline: true
required: true
label: Lovely Animals
help_text: Please choice your favorite animal

インライン化されたMultipleCheckboxField
ChoiceRequiredField¶
-
class
ChoiceRequiredField
(choices, *args, **kwargs)¶
0.1.12 で登場しました。
選択必須のリストを提供します。
基本的な動作は ChoiceField
と変わりませんがユーザーに選択を促すブランクチョイスを入れるため、 choices
の1つ目の値が必ずnull値である必要があります。
このクラスは restframeworkの ChoiceField
を継承してつくられています。その他のオプションについては
ChoiceField を参照してください。
main:
name: YourFavoriteAnimal
fields:
- name: animal_choice_field
field: definable_serializer.extra_fields.ChoiceWithBlankField
field_args:
- - - null
- "-------- Please Choice one 😉 --------"
- - dog
- 🐶Dog
- - cat
- 😺Cat
- - rabbit
- 🐰Rabbit
field_kwargs:
label: Lovely Animals
blank_label: '-------- Please Choice 😉 --------'
help_text: Please choice your favorite animal
ChoiceWithBlankField¶
警告
ChoiceWithBlankFieldクラスは廃止予定です。変わりに ChoiceRequiredField を利用してください。
-
class
MultipleCheckboxField
(choices, *args, blank_label=None, **kwargs)
渡されたchoicesの選択にブランクチョイスを自動的に追加します。ブランクチョイスが選択された状態でバリデーションが 行われるとエラーになります。
fieldには definable_serializer.extra_fields.ChoiceWithBlankField
を指定します。
blank_label
に文字列を渡すとダッシュの連続の代わりにその文字列がブランクチョイスの部分に表示されます。
このクラスは restframeworkの ChoiceField
を継承してつくられています。その他のオプションについては
ChoiceField を参照してください。
main:
name: YourFavoriteAnimal
fields:
- name: animal_choice_field
field: definable_serializer.extra_fields.ChoiceWithBlankField
field_args:
- - - dog
- 🐶Dog
- - cat
- 😺Cat
- - rabbit
- 🐰Rabbit
field_kwargs:
label: Lovely Animals
blank_label: '-------- Please Choice 😉 --------'
help_text: Please choice your favorite animal

blank_labelに文字を渡した例。blank_labelが空の場合は "---------" となります。
RadioField¶
-
class
RadioField
(choices, *args, inline=False, **kwargs)¶
ラジオボタンによる選択肢を表示するフィールドを提供します。
fieldには definable_serializer.extra_fields.RadioField
を指定します。
inline
をtrue
にするとチェックボックスが横並びに表示されます。
このクラスは restframeworkの ChoiceField
を継承してつくられています。その他のオプションについては
ChoiceField を参照してください。
main:
name: YourFavoriteAnimal
fields:
- name: animal_choice_field
field: definable_serializer.extra_fields.RadioField
field_args:
- - - dog
- 🐶Dog
- - cat
- 😺Cat
- - rabbit
- 🐰Rabbit
field_kwargs:
inline: true
required: true

インライン化されたRadioField
TextField¶
警告
TextFieldクラスは廃止予定です。変わりに CharField を利用してstyle引数を渡してください。 詳しくは field-styles を参照してください。
また、placeholder引数は フィールドの国際化 翻訳の対象になりません。
テキストエリアを提供します。
fieldには definable_serializer.extra_fields.TextField
を指定します。
rows
に数値を渡すことででテキストエリアの行数を指定することができます。placeholder
に文字列を渡すとプレースホルダー文字列を表示することができます。
このクラスは restframeworkの CharField
を継承してつくられています。その他のオプションについては
CharField を参照してください。

placeholderとrowsを設定した例
提供する関数¶
definable-serializerでは、YAMLやJSONで記述された定義からシリアライザーを作成する5つの関数を提供しています。
- build_serializer
- build_serializer_by_json
- build_serializer_by_json_file
- build_serializer_by_yaml
- build_serializer_by_yaml_file
build_serializer関数¶
-
build_serializer
(definition, base_classes=[], allow_validate_method=True)¶
build_serializer
は定義が記述されたPythonのDictからシリアライザークラスを作成します。
base_classes
継承するクラスを指定することができます。
allow_validate_method
が False
の場合、シリアライザーの定義中に validate_method
が記述されていると ValidationError
が発生します。
>>> from definable_serializer.serializers import build_serializer
>>> serializer_definition = {
... "main": {
... "name": "TestSerializer",
... "fields": [
... {
... "name": "test_field",
... "field": "CharField",
... "field_kwargs": {
... "max_length": 100,
... }
... }
... ],
... "validate_method": """def validate_method(self, value):
... return value
... """
... },
... }
# allow_validate_methodがTrueの場合
>>> serializer_class = build_serializer(serializer_definition, allow_validate_method=True)
>>> serializer_class()
TestSerializer():
test_field = CharField(max_length=100)
# allow_validate_methodがFalseの場合
>>> serializer_class = build_serializer(serializer_definition, allow_validate_method=False)
ValidationError: ['serializer validate_method not allowed.']
build_serializer_by_json関数¶
-
build_serializer_by_json
(definition, base_classes=[], allow_validate_method=True)¶
build_serializer_by_json
は定義が記述されたJSON文字列からシリアライザークラスを作成します。
base_classes
継承するクラスを指定することができます。
allow_validate_method
が False
の場合、シリアライザーの定義中に validate_method
が記述されていると ValidationError
が発生します。
>>> from definable_serializer.serializers import build_serializer_by_json
>>> json_str = """
... {
... "main": {
... "name": "TestSerializer",
... "fields": [
... {
... "name": "test_field",
... "field": "CharField",
... "field_kwargs": {"max_length": 100}
... }
... ],
... "validate_method": "def validate_method(self, value):\\n return value\\n "
... }
... }
... """
# allow_validate_methodがTrueの場合
>>> serializer_class = build_serializer_by_json(json_str, allow_validate_method=True)
>>> serializer_class()
TestSerializer():
test_field = CharField(max_length=100)
# allow_validate_methodがFalseの場合
>>> serializer_class = build_serializer_by_json(json_str, allow_validate_method=False)
ValidationError: ['serializer validate_method not allowed.']
build_serializer_by_json_file関数¶
-
build_serializer_by_json_file
(json_filepath, base_classes=[], allow_validate_method=True)¶
build_serializer_by_json_file
は定義が記載されたJSONファイルからシリアライザークラスを作成します。
この関数の動作はファイルパスを受け取る以外、 build_serializer_by_json
関数と同等です。
build_serializer_by_yaml関数¶
-
build_serializer_by_yaml
(definition, base_classes=[], allow_validate_method=True)¶
build_serializer_by_json
定義が記述されたYAML文字列からシリアライザークラスを作成します。
base_classes
継承するクラスを指定することができます。
allow_validate_method
が False
の場合、シリアライザーの定義中に validate_method
が記述されていると ValidationErrorが発生します。
>>> from definable_serializer.serializers import build_serializer_by_yaml
>>> yaml_str = """
... main:
... name: "TestSerializer"
... fields:
... - name: test_field
... field: CharField
... field_kwargs:
... max_length: 100
... validate_method: |
... def validate_method(self, value):
... return value
... """
# allow_validate_methodがTrueの場合
>>> serializer_class = build_serializer_by_yaml(yaml_str, allow_validate_method=True)
>>> serializer_class()
TestSerializer():
test_field = CharField(max_length=100)
# allow_validate_methodがFalseの場合
>>> serializer_class = build_serializer_by_yaml(yaml_str, allow_validate_method=False)
ValidationError: ['serializer validate_method not allowed.']
その他の有用な情報¶
ここではいくつかの有用な情報及び注意点などを記載します。
definable-serializer-exampleについて¶
プロジェクトにdefinable-serializerを組み込む で作成したサンプルプロジェクトになります。 実際に動作するコードを参照したい場合はこちらを御覧ください。
https://github.com/salexkidd/restframework-definable-serializer-example
definable-serializerのつかいどころ¶
入力フィールドの変更が多いシリアライザーから可哀想なモデルを解放するのがdefinable-serializerの役割です。 逆にフィールドの変更が少ないシリアライザーはモデルシリアライザーを用いて作成するべきです。
柔軟性はシステムに変更のチャンスを与えますが、過ぎたる柔軟性は土台を揺るがしシステムの崩壊時期を早めます。
definable-serializerが向いているのは、入力内容が場合によって変更されるものの1つのモデルクラスで取り扱いたい場合や、モデルのメタ情報を扱う場合、そしてモックを作る場合です。
それ以外での利用は十分に注意し、考えた末で利用を検討するべきです。
JSONFieldでデータを扱う場合の問題点¶
サードパッケージ及びdjangoが提供するJSONFieldは有用なモデルフィールドであるものの、利用するには2つの問題があります。
1つはJSONEncoderの問題、もうひとつが非ASCII文字列の問題です。
そのためにdefinable-serializerではいくつかのフィールドを提供しています(詳しくは 提供するモデルフィールドクラス を参照してください)。
しかし、場合によっては提供するモデルフィールドを利用できないケースもあります。
特に、postgreSQLを利用しておりユーザーからの入力データを検索対象にしたいのならば、djangoが提供する
django.contrib.postgres.fields.JSONField
利用するのがベストです。
もし、MySQLを利用しているのならば django-mysql を利用するとよいでしょう。
ただし、これらのJSONFieldにはJSONEncoder、及び非ASCII文字に関する問題があります。
もし、definable-serializerが提供する以外のJSONFieldを利用するにはこれらの問題に対処する必要があります。
JSONEncoder問題¶
PythonでデータをJSONにシリアライズする場合、大きな落とし穴があります。
それは、ネイティブデータ型である set型
がpythonに付属するjsonモジュールではエンコードすることができないからです。
set型
を含むデータをjson化すると`` TypeError`` が発生するのを確認できます。
>>> import json
>>> json.dumps(set([1,2,3]))
TypeError: Object of type 'set' is not JSON serializable
困ったことに、restframeworkが提供するMultipleChoiceフィールドはバリデーション後の結果を
set型
で返すため、そのままJSONFieldに値を渡すとエラーが発生してしまいます。
この問題を解決するには DjangoJSONEncoder
を継承して自作のEncoderクラス用意し、 set型
のデータを list型
に変換するように変更します。
from django.db import models
from django.core.serializers.json import DjangoJSONEncoder
from django.contrib.postgres.fields import JSONField
class MyCustomJSONEncoder(DjangoJSONEncoder):
def default(self, o):
if isinstance(o, set):
return list(o)
else:
return super().default(o)
class TestModel(models.Model):
answer = JSONField(encoder=MyCustomJSONEncoder)
非ASCII文字列問題¶
以下のJSON文字列を見てみましょう
{"favorite_food": "\ud83c\udf54"}
これは、ハンバーガー(🍔)のEmojiです。しかし、'\ud83c\udf54' は全く美味しそうに見えません。 目に見る必要がないデータならばこれで問題ありませんが、adminサイトで入力されたデータを確認しようとして、"\ud83c\udf54" のような文字列が表示されたらどうでしょうか。
エンジニアならばこの文字列をデコードして意味を知ることができるかもしれません。 しかし、実際にデータを扱うオペレーターから見ると不吉な何かにしか見えないでしょう。

ハンバーガー的な何か
この問題を避けるには、eusure_ascii
オプションを False
にしてdumpを行う必要があります。
以下にコード例を示します。
>>> import json
>>> input_data = {
... "favorite_food": "🍔"
... }
>>> json.dumps(input_data)
'{"favorite_food": "\\ud83c\\udf54"}'
>>> json.dumps(input_data, ensure_ascii=False)
'{"favorite_food": "🍔"}'
ensure_ascii
を False
にしたい場合、モデルフィールドのソースコードを読み、各自で json.dumps
の部分を変更してオプションを渡すようにしなければなりません。
JSONFieldの供給過多問題¶
JSONFieldにはもう1つ問題があります。世界中のエンジニアはJSONを好んで利用します。 その結果、Googleで調べるといくつものJSONFieldがdjangoに提供されていることが確認できます。
- https://pypi.python.org/pypi/jsonfield
- https://pypi.python.org/pypi/django-jsonfield
- https://pypi.python.org/pypi/django-json-field
- http://django-mysql.readthedocs.io/en/latest/model_fields/json_field.html
また、djangoも django.contrib.postgres.fields.JSONField
を提供しています。
ハッキリ言えば供給が多すぎて、どれを利用してよいか迷ってしまいます。
きっと優秀なあなたならば間違えないでしょう。しかし、筆者はpipでインストールを行う際に十中八九間違えます。 (余談ながら、上記パッケージの大半が JSONEncoder問題 及び 非ASCII文字列問題 を抱えています。)
これらの問題に一番対処しやすいのが django-jsonfield (上記リストの先頭)です。
フィールドの引数に対して `dump_kwargs
を渡すことで、JSONEncoder及びensucre_ascii問題に対処することができます。
definable-serializerでは、 DefinableSerializerByJSONField および JSONField においてdjango-jsonfieldを利用しています。
各種配布先¶
Todo¶
TodoはGithub上で管理しています。
連絡先¶
twitter: @salexkidd
ライセンス¶
Copyright 2017 salexkidd
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.