DEV Community

kaede
kaede

Posted on • Edited on

Django REST FRAMEWORK Tutorial 4 -- Snippet アプリのシリアライザの使い方を bash で確認する

このチュートリアルアプリを作るまで

https://dev.to/kaede_io/django-rest-framework-no-tutorial-1-serializer-de-serializerdata-woque-ren-suru-58k9

この記事で 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)
>>> 
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

Snippet をモデルからインポート
Snippetシリアライザもインポート
JSON Renderer, Parser をインポート

>>> snippet = Snippet(code='foo = "bar"\n')
>>> snippet.save()

>>> snippet = Snippet(code='print("hello, world")\n')
>>> snippet.save()
Enter fullscreen mode Exit fullscreen mode

Snippet モデルで code を入れたものを snippet 変数に入れる
そして保存、これらを二回繰り返す。

>>> serializer = SnippetSerializer(snippet)
Enter fullscreen mode Exit fullscreen mode

そしてシリアライズをかける。
シリアライズはデータを JSON でフロントに渡すときの橋渡しの型だと解釈している。


シリアライズされたデータを確認する

>>> serializer.data

{'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}

Enter fullscreen mode Exit fullscreen mode

中身の 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"}'
Enter fullscreen mode Exit fullscreen mode

"hoge": "foo"

のダブルクォートでの見慣れた形に変更できた


io を使って BytesIO オブジェクトにする

import io
stream = io.BytesIO(content)
stream
<_io.BytesIO object at 0xffff80986a70>
Enter fullscreen mode Exit fullscreen mode

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'}
Enter fullscreen mode Exit fullscreen mode

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'), 
Enter fullscreen mode Exit fullscreen mode

オブジェクトインスタンスを 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.
Enter fullscreen mode Exit fullscreen mode

シリアライザの .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()`.
Enter fullscreen mode Exit fullscreen mode

いきなり save しようとしても is_valid をかけろと言われる。
中身があるかの確認的な?

serializer.is_valid()
True

serializer.save()
<Snippet: Snippet object (5)>
Enter fullscreen mode Exit fullscreen mode

Snippet モデルに 5 つ目として保存された。
これでデータベースに永続化されたと解釈する。

ちゃんと順番通りにしないと


.validated_data と .initial_data でシリアライズされたものの中身を見る

>>> serializer.validated_data
OrderedDict([('title', ''), ('code', 'print("hello, world")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
Enter fullscreen mode Exit fullscreen mode

.validated_data を使うと orderedDict, 並び替えられた辞書型で出力できる。

>>> serializer.initial_data
{'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}
Enter fullscreen mode Exit fullscreen mode

一応イニシャルデータとしてならそのまま出力できる。
これらは .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')])]
Enter fullscreen mode Exit fullscreen mode

先に作った 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')
Enter fullscreen mode Exit fullscreen mode

これを省略する処理をする。

誤字や解釈の間違いがあったらコメントお願いします。

Top comments (0)