Django REST FRAMEWORK Tutorial 4 -- Snippet アプリのシリアライザの使い方を bash で確認する
kaede
Posted on February 14, 2022
このチュートリアルアプリを作るまで
この記事で Django REST Framework のチュートリアルアプリを Docker-Compose で作成するところまで進めた
bash でテストする
https://www.django-rest-framework.org/tutorial/1-serialization/#working-with-serializers
docker-compose run web \
python manage.py \
shell
Creating rest5_web_run ... done
Python 3.10.1 (main, Dec 21 2021, 06:15:32) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
shell を動かし、python のインタラクティブコンソールが起動できた。
コードを Snippet モデルで保存し、シリアライズをかける
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
Snippet をモデルからインポート
Snippetシリアライザもインポート
JSON Renderer, Parser をインポート
>>> snippet = Snippet(code='foo = "bar"\n')
>>> snippet.save()
>>> snippet = Snippet(code='print("hello, world")\n')
>>> snippet.save()
Snippet モデルで code を入れたものを snippet 変数に入れる
そして保存、これらを二回繰り返す。
>>> serializer = SnippetSerializer(snippet)
そしてシリアライズをかける。
シリアライズはデータを JSON でフロントに渡すときの橋渡しの型だと解釈している。
シリアライズされたデータを確認する
>>> serializer.data
{'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}
中身の data を見ると
- id が 2
- code が
'print("hello, world")\n'
- title, lineos, language, style,
の 2 つ目のデータが見える。
python はデフォルトなので、解析できているかは不明。
これは python オブジェクトになっていると解釈する
ここから JSON にして戻して Snippet シリアライザに戻す。
JSON レンダラーで シリアライズしたデータを JSON に変換して出力する
>>> content = JSONRenderer().render(serializer.data)
>>> content
b'{"id":2,"title":"","code":"print(\\"hello, world\\")\\n","linenos":false,"language":"python","style":"friendly"}'
"hoge": "foo"
のダブルクォートでの見慣れた形に変更できた
io を使って BytesIO オブジェクトにする
import io
stream = io.BytesIO(content)
stream
<_io.BytesIO object at 0xffff80986a70>
io をインポートして BytesIO にかける。
この時点では BytesIO の 16 進数のオブジェクト
Python native datatypes
らしい。
JSON パーサーで BytesIO オブジェクトを python オブジェクトにする
data = JSONParser().parse(stream)
data
{'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}
JSON パーサーを掛けると、BytesIO のものを python オブジェクトに変換できる
fully populated object instance らしい。
python オブジェクトの data をシリアライザにかける
serializer = SnippetSerializer(data=data)
serializer
SnippetSerializer(data={'id': 4, 'title': '', 'code': 'print=("Hello Django")', 'linenos': False, 'language': 'python', 'st
yle': 'friendly'}):
id = IntegerField(read_only=True)
title = CharField(allow_blank=True, max_length=100, required=False)
code = CharField(style={'base_template': 'textarea.html'})
linenos = BooleanField(required=False)
language = ChoiceField(choices=[('abap', 'ABAP'),
オブジェクトインスタンスを data として Snippet シリアライザをかける
中身を見ると
データの他に、カラムに入るもののデータがシリアライザに入っている。
シリアライザにかけたものの data を見て save に失敗する
serializer.data
serializer.save()
AssertionError:
You cannot call `.save()` after accessing `serializer.data`.
If you need to access data before committing to the database
then inspect 'serializer.validated_data' instead.
シリアライザの .data にアクセスした後には .save() できないらしい。
なのでデータベースにコミット(保存)する前に中身を覗きたかったら、
.data を安易に見ないで、serializer.validated_data で除くべきらしい
serializer.data is serializing the unsaved instance,
because you've called it prior to save.
保存されていないデータを serializer.data は暗号化(シリアライズ)してしまうので、そのあと保存することができなくなるらしい。
これの data を見てしまうと .save することができなくなり、前の手順からやり直しになる。
シリアライズしたものに is_valid()
をかけて save() をする
>>> serializer.save()
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/local/lib/python3.10/site-packages/rest_framework/serializers.py", line 177, in save
assert hasattr(self, '_errors'), (
AssertionError: You must call `.is_valid()` before calling `.save()`.
いきなり save しようとしても is_valid をかけろと言われる。
中身があるかの確認的な?
serializer.is_valid()
True
serializer.save()
<Snippet: Snippet object (5)>
Snippet モデルに 5 つ目として保存された。
これでデータベースに永続化されたと解釈する。
ちゃんと順番通りにしないと
.validated_data と .initial_data でシリアライズされたものの中身を見る
>>> serializer.validated_data
OrderedDict([('title', ''), ('code', 'print("hello, world")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
.validated_data を使うと orderedDict, 並び替えられた辞書型で出力できる。
>>> serializer.initial_data
{'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}
一応イニシャルデータとしてならそのまま出力できる。
これらは .save をかける前にも確認することができる。
.data とは違ってこれらで確認した後でも .save ができるらしい。未検証。
Snippet の中のオブジェクトを全てシリアライズかける
serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
[OrderedDict([('id', 1), ('title', ''), ('code', 'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print("hello, world")\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', ''), ('code', 'var="hoge"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 4), ('title', ''), ('code', 'print=("Hello Django")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 5), ('title', ''), ('code', 'print=("Hello Django")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]
先に作った Snippet モデルの中のオブジェクトを many=True で複数指定して Snippetシリアライザにかける。
シリアライザしたものを .data でアクセスすると Order Dict で出力される。
おそらくこれで Response に渡せる状態になった。
次の章でやること
https://www.django-rest-framework.org/tutorial/1-serialization/#using-modelserializers
次の章はモデルシリアライザ。
次はモデルとほぼ同じことをしているシリアライザのファイルのカラムの定義
class SnippetSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(required=False, allow_blank=True, max_length=100)
code = serializers.CharField(style={'base_template': 'textarea.html'})
linenos = serializers.BooleanField(required=False)
language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')
これを省略する処理をする。
誤字や解釈の間違いがあったらコメントお願いします。
Posted on February 14, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.