Django REST FRAMEWORK Tutorial 4 -- Snippet アプリのシリアライザの使い方を bash で確認する
Tags: python, django, serializer
Published: 2022-02-14 14:10:00



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

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


```php
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 モデルで保存し、シリアライズをかける

```py
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 をインポート

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

>>> snippet = Snippet(code='print("hello, world")\n')
>>> snippet.save()
```

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

```py
>>> serializer = SnippetSerializer(snippet)
```

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

---

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

```py
>>> 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 に変換して出力する

```py
>>> 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 オブジェクトにする

```py
import io
stream = io.BytesIO(content)
stream
<_io.BytesIO object at 0xffff80986a70>
```

io をインポートして BytesIO にかける。
この時点では BytesIO の 16 進数のオブジェクト

`Python native datatypes` らしい。

---

## JSON パーサーで BytesIO オブジェクトを　python オブジェクトにする

```py
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 をシリアライザにかける

```py
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 に失敗する

```py
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() をする

```py
>>> 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 をかけろと言われる。
中身があるかの確認的な？

```py
serializer.is_valid()
True

serializer.save()
<Snippet: Snippet object (5)>
```

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

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

---

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

```py
>>> serializer.validated_data
OrderedDict([('title', ''), ('code', 'print("hello, world")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
```

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

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

一応イニシャルデータとしてならそのまま出力できる。
これらは .save をかける前にも確認することができる。
.data とは違ってこれらで確認した後でも .save ができるらしい。未検証。

---

## Snippet の中のオブジェクトを全てシリアライズかける

```py
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

次の章はモデルシリアライザ。

次はモデルとほぼ同じことをしているシリアライザのファイルのカラムの定義

```py
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')
```

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



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