Django REST framework（DRF）公式チュートリアル① Serialization

# Tutorial 1: Serialization

### はじめに
本記事は、Django REST framework の公式チュートリアル[Tutorial 1: Serialization](https://www.django-rest-framework.org/tutorial/1-serialization/)の内容をもとに、和訳および補足解説を行ったものです。

基本的にはチュートリアルの翻訳を軸に構成しており、`✏️補足`の箇所では、各コードセクションの補足や内部処理の解説を備忘録的に記載しています。

他のチュートリアル記事もあわせて読みたい方は、以下のまとめ記事からご覧いただけます👇  
[DRF（Django REST framework）公式チュートリアルを日本語でまとめてみた【全6回】](https://qiita.com/yutty-kov/items/1850f3e865ec1a50a7bb)

それでは、本チュートリアルを始めましょう。
- - -

### イントロダクション
このチュートリアルでは、シンプルなPastebin風のコードハイライトWeb APIの作成を扱います。その過程で、REST frameworkを構成するさまざまなコンポーネントを紹介し、すべての仕組みがどのように組み合わさっているのかを包括的に理解できるようになります。

このチュートリアルはかなり詳しく書かれているので、始める前にクッキーとお気に入りの飲み物を用意するのがおすすめです。
ざっと全体像を知りたいだけなら、代わりに[Quickstart](https://www.django-rest-framework.org/tutorial/quickstart/)ドキュメントを読むとよいでしょう。

:::note info
**補足**: このチュートリアルのコードはGitHubの[encode/rest-framework-tutorial](https://github.com/encode/rest-framework-tutorial)リポジトリで利用できます。気軽にリポジトリをクローンして、コードを動かしてみてください。
:::

### 新しい環境のセットアップ
このチュートリアルを始める前に `venv` を使用して新しい仮装環境を構築します。
これにより他のプロジェクトとは独立して、パッケージの構成をきれいに保つことができます。
```
python3 -m venv env
source env/bin/acttivate
```

新しく構築した環境内にいるため、必要なパッケージをインストールします。
```
pip install django
pip install djangorestframework
pip install pygments  # コードのハイライトにこれを使います
```
:::note info
この仮想環境から出るには、<font color="#d14">`deactivate`</font> と入力してください。より詳細な情報は[venv documentation](https://docs.python.org/3/library/venv.html)を確認してください。
:::

### はじめに
それではコーディングする準備が整ったので、新しくプロジェクトを作成しましょう。
```
cd ~
django-admin startproject tutorial
cd tutorial
```
新規プロジェクトの作成が完了したら、シンプルなWeb APIを作るためのアプリを作りましょう。
```
python manage.py startapp snippets
```
上記で新しく作成した <font color="#d14">`snippets`</font> アプリと <font color="#d14">`rest_framework`</font> アプリを <font color="#d14">`INSTALLED_APPS`</font> に追加する必要があります。
それでは <font color="#d14">`tutorial/settings.py`</font> を編集しましょう:
```python:tutorial/settings.py
INSTALLED_APPS = [
    ...
    
    'rest_framework',
    'snippets',
]
```
これで準備は完了です。

### 作業するModelの作成
このチュートリアルでは、コードスニペットを保存するためのシンプルなSnippetモデルを作成するとこから始めます。`snippets/models.py` ファイルを編集してください。
**補足**: 良いプログラミングの習慣はコメントを書くことです。[リポジトリ](https://github.com/encode/rest-framework-tutorial)の本チュートリアルのバージョンでコメントを見ることができますが、ここではコードのみに集中するためコメントは省略しています。
```python:snippets/models.py
from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles

LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()])


class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)

    class Meta:
        ordering = ['created']
```

上記のコードを実装後、Snippetモデルの最初のマイグレーションを作成し、データベースを同期する必要があります。
```
python manage.py makemigrations snippets
python manage.py migrate snippets
```

## ✏️補足

Snippetモデル（`snippets/models.py`）で記述されているコードの一部を説明したいと思います。

Pygmentsという構文ハイライターライブラリからそれぞれ以下（コメント参照）をインポートしています。
```python
from pygments.lexers import get_all_lexers # 言語の構文解析
from pygments.styles import get_all_styles # スタイル(色や見た目)
```
Pythonを例にした `get_all_lexers()` の戻り値は以下のようになるため、`if item[1]` で言語の有無を確認し、存在するもののみ `LEXERS` に代入します。
`('Python', ['python', 'py'], ['*.py'], ['text/x-python'])`
`言語の正式名称, エイリアス, 対応する拡張子, 対応MINE type`
```python
LEXERS = [item for item in get_all_lexers() if item[1]]
```
`LANGUAGE_CHOICES` には、`LEXERS` からエイリアス（例: 'python'）と 言語の正式名称（例: 'Python'）のみを抽出し、それらをソートして代入します。
（sorted() 関数は、引数としてリストやタプルを受け取った場合、各要素の第1要素（この場合はエイリアスの 'python'）を基準にソートします。）
```python
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
```
<br>

`STYLE_CHOICES` には利用可能なスタイルをタプルのリストに変換し、代入しています。
`get_all_styles()` は ``'default', 'emacs', 'friendly', 'colorful'`` のようなスタイル名（文字列）を順に返します。
そのため、各スタイル名を ``(item, item)`` の形でタプルにし、`STYLE_CHOICES` に追加しています。
このように1つの値を2つの要素に分けてタプルにしている理由は、
``style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)`` の `choices` 引数が、
「①内部に保存する値」と「②フォームや管理画面で表示されるラベル」のペアを必要とするためです。（`LANGUAGE_CHOICES` も同じ意図で2つの要素のタプルにしています）
今回は、スタイル名がそのまま表示名としても十分わかりやすいため、両方に同じ値を使っています。

```python:
STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()])
```
- - -
### Serializerクラスの作成
本チュートリアルでWeb APIを作成するにあたり、最初に行うべきことは、SnippetインスタンスをJSONなどの形式にシリアライズ（変換) および デシリアライズ（元に戻す） する方法を用意することです。
これは、Djangoのフォームに似た動作をする Serializer を定義することで実現できます。
`snippets` ディレクトリ内に `serializers.py` という名前のファイルを作成し、以下のコードを追加してください。

```python:snippets/serializers.py
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES


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

    def create(self, validated_data):
        """
        バリデーションが通ったデータをもとに、新しいSnippetインスタンスを作成して返します。
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        バリデーションが通ったデータをもとに、既存のSnippetインスタンスを更新して返します。
        """
        instance.title = validated_data.get('title', instance.title)
        instance.code = validated_data.get('code', instance.code)
        instance.linenos = validated_data.get('linenos', instance.linenos)
        instance.language = validated_data.get('language', instance.language)
        instance.style = validated_data.get('style', instance.style)
        instance.save()
        return instance
```
Serializerクラスの最初の部分では、シリアライズ／デシリアライズ されるフィールドを定義します。
`create()` と `update()`メソッドは、`serializer.save()` が呼び出されたときに、どのようにインスタンスが作成 または 更新されるかを定義します。

Serializerクラスは Django のフォームクラスと非常によく似ており、`required`、`max_length`、`default`など、さまざまなフィールドに対するバリデーション用のフラグを持っています。

フィールドのフラグは、HTMLとしてレンダリングされる場合など、特定の状況においてSerializerがどのように表示されるべきかを制御することもできます。
上記の `{'base_template': 'textarea.html'}` というフラグは、Djangoのフォームクラスにおける `widget=widgets.Textarea` を指定するのと同じ意味です。
これらのフラグは、チュートリアルの後半で登場する browsable API（ブラウズ可能なAPI）において、表示方法を制御するのに特に役立ちます。

実は、後ほど説明する `ModelSerializer` クラスを使えばこれらのコードを書く手間を省くこともできるのですが、ここでは Serializer の定義を明示的に行うことにします。


## ✏️補足
Snippetモデル（`snippets/models.py`）で定義した`LANGUAGE_CHOICES` と `STYLE_CHOICES` をここでも `choices` に使用していますね。

```python:
class SnippetSerializer(serializers.Serializer):
    language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
    style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')
```

SnippetモデルとSerializerクラスで使用用途の違いは以下になります。
| Snippetモデル | Serializerクラス |
|:-:|:-:|
|  データベースに保存する値の制限 | APIリクエストのバリデーションを行う  |
|  Django 管理画面などで選択肢として表示させるため | Brousable API（チュートリアル後半で使用）でのフォーム表示や選択肢の表示のため  |
|   | モデルと無関係な独自のシリアライザを使う場合にも対応可能  |


また、`create()` と `update()` は、後述する Serializerオブジェクトの `save()` を呼んだときに、インスタンスをどのように「新規作成」または「更新」して保存するかを定義するメソッドになります。

`create()` ではバリデーションが通ったデータは`validated_data` という辞書型の変数に格納されます。この辞書はフィールド名をキーとして持つため、`**validated_data` とアンパックすることで、各フィールドに対応する値がキーワード引数として `Snippet.objects.create()` に渡され、モデルの新しいインスタンスが作成・保存されます。
```python:
    def create(self, validated_data):
        return Snippet.objects.create(**validated_data)
```

`update()`メソッドには `validated_data` のほかに ``SnippetSerializer(instance, data=xx)`` のように `instance` も渡されます。
この `instance` には、以下のように Serializer を呼び出すときに第1引数として渡されたModelオブジェクトが入ります。
これは主に「更新（update）」処理を行いたいときに使います。

```python:
serializer = SnippetSerializer(snippet, data=data)
```
`instance.xx = validated_data.get('xx', instance.xx)` は、Modelオブジェクトの各フィールドを更新する処理です。
`validated_data` は辞書型なので、`get()`メソッドを使って、指定したキー（ここでは `'title'`）の値を取得しています。
もしそのキーが存在しない（更新データがない）場合は、代わりに既存の値（`instance.title`）を使って、元の値を維持するようにしています。
```python:
instance.title = validated_data.get('title', instance.title)
```
このようにして、更新用のデータが存在するものだけが書き換えられるようになっています。

```python:
    def update(self, instance, validated_data):
        instance.title = validated_data.get('title', instance.title)
        instance.code = validated_data.get('code', instance.code)
        instance.linenos = validated_data.get('linenos', instance.linenos)
        instance.language = validated_data.get('language', instance.language)
        instance.style = validated_data.get('style', instance.style)
        instance.save()
        return instance
```
- - -
### Serializerの操作
チュートリアルを進める前に、シリアラザクラスの使い方について慣れておきましょう。それではDjangoシェルに入ってみましょう。
```
python manage.py shell
```

いくつかインポートを済ませたら、実際に操作するためのコードスニペットをいくつか作成しましょう。

```python:
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser

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

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

これで、スニペットインスタンスをいくつか用意できました。それでは、これらから1つのインスタンスをシリアライズしてみましょう。

```python:
serializer = SnippetSerializer(snippet)
serializer.data
# {'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}
```
この時点で、ModelインスタンスをPythonが扱う基本的なデータ型に変換しました。シリアライズ処理を完了させるために、このデータをJSONに変換します。

```python:
content = JSONRenderer().render(serializer.data)
content
# b'{"id":2,"title":"","code":"print(\\"hello, world\\")\\n","linenos":false,"language":"python","style":"friendly"}'
```

デシリアライズも類似しており、まずはストリームをPythonの基本的なデータ型にパース(構文解析)して変換します。

```python:
import io

stream = io.BytesIO(content)
data = JSONParser().parse(stream)
```

そして、これら（`data`）のPythonのデータを 完全なModelインスタンス（フィールドに値がしっかり入ったインスタンス）として復元します。

```python:
serializer = SnippetSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# {'title': '', 'code': 'print("hello, world")', 'linenos': False, 'language': 'python', 'style': 'friendly'}
serializer.save()
# <Snippet: Snippet object>
```

このAPIがフォームを扱う流れに非常に似ていることに注目してください。この類似点は、Serializerを使ったViewの記述時にさらに明確になります。

Modelインスタンスに加えて、QuerySetをシリアライズすることも可能です。これを行うには、Serializerの引数に `many=True` を追加します。
```python:
serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
# [{'id': 1, 'title': '', 'code': 'foo = "bar"\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}, {'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}, {'id': 3, 'title': '', 'code': 'print("hello, world")', 'linenos': False, 'language': 'python', 'style': 'friendly'}]
```
## ✏️補足
**Serializerの操作**のPythonシェルで行ったシリアライズとデシリアライズの流れは以下になります。

```:
【シリアライズ】
・Snippetオブジェクト(モデルインスタンス)
    ↓
・Snippetオブジェクト
   # serializer = SnippetSerializer(snippet)
    ↓
・Pythonの辞書型データ
   # serializer.data
    ↓
・JSON

【デシリアライズ】
・JSON
    ↓
・Pythonの辞書型データ
   # data = JSONParser().parse(stream)
    ↓
・Serializerオブジェクト
   # serializer = SnippetSerializer(data=data)
    ↓
・Snippetオブジェクト(モデルインスタンス)
```
<br>

- - -
### ModelSerializersの使用
`SnippetSerializer`クラスは `Snippet`モデルにも含まれている多くの情報を繰り返し定義しています。
これらのコードをもう少し簡潔にできたら理想的です。

Djangoが `Form`クラス と `ModelForm`クラスの両方を提供しているのと同様に、REST frameworkは `Serializer`クラス と `ModelSerializer`クラスの両方を提供しています。

それでは、`ModelSerializer`クラスを使ってSerializerをリファクタリングしてみましょう。
もう一度  `snippets/serializers.py`ファイルを開き、`SnippetSerializer`クラスを次のコードに置き換えます。

```python:
class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ['id', 'title', 'code', 'linenos', 'language', 'style']
```

Serializerの便利な特性のひとつは、`Serializer`インスタンス内のすべてのフィールドを簡単に確認できることです。

```python:
from snippets.serializers import SnippetSerializer
serializer = SnippetSerializer()
print(repr(serializer))
# SnippetSerializer():
#    id = IntegerField(label='ID', 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=[('Clipper', 'FoxPro'), ('Cucumber', 'Gherkin'), ('RobotFramework', 'RobotFramework'), ('abap', 'ABAP'), ('ada', 'Ada')...
#    style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')...
```

押さえとくべきポイントは、`ModelSerializer`クラスは何か特別なことをしているわけではなく、単にSerializerクラスを作るためのショートカットにすぎません：
・自動的に決定されるフィールドのセット
・`create()` と `update()`のシンプルなデフォルト実装

## ✏️補足
ModelSerializer クラスを使うと、対象のModelに基づいてSerializerを簡潔に定義できます。
`Meta` クラスで `model` に対象のModelを指定し、`fields` にシリアライズ対象のフィールド名を列挙することで、必要なフィールドを自動的に定義してくれます。

さらに、本文でも説明されているように、`create()` や `update()` メソッドもModelに応じた適切な内容で自動的に実装されるため、自分でコードを書く必要はなく、省略することができます。

以下に `ModelSerializer` と `Serializer` を使った場合の実装の違いを示します。

```python:
# ModelSerializerクラスを使った実装
class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ['id', 'title', 'code', 'linenos', 'language', 'style']

# Serializerクラスを使った実装
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')

    def create(self, validated_data):
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        instance.title = validated_data.get('title', instance.title)
        instance.code = validated_data.get('code', instance.code)
        instance.linenos = validated_data.get('linenos', instance.linenos)
        instance.language = validated_data.get('language', instance.language)
        instance.style = validated_data.get('style', instance.style)
        instance.save()
        return instance
```
ModelSerializer は多くの処理を自動化してくれるため、わざわざ自分でコードを書く必要がなく、Serializerをより少ない・簡潔なコードで定義できるというメリットがあります。そのため、実務でもよく使われることが多いそうです。

- - -
### Serializer を使った通常の Django ビューの作成
それでは、新しい`Serializer`クラスを使ってAPIビューを書く方法を見ていきましょう。この時点ではREST frameworkの他の機能は使用しないため、通常のDjangoのViewを書きましょう。

`snippets/views.py`ファイルを編集して、以下の内容を追加しましょう。
```python:
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
```

このAPIのルート（最上位）には、既存のすべてのSnippetを一覧表示するか、新しいSnippetを作成するViewを配置します。
```python:
@csrf_exempt
def snippet_list(request):
    """
    すべてのコードスニペットを表示する、または新しいスニペットを作成する
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return JsonResponse(serializer.data, safe=False)

    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data, status=201)
        return JsonResponse(serializer.errors, status=400)
```

このViewに対して、CSRFトークンを持たないクライアントからも POSTできるようにしたいため、このビューには csrf_exempt を付けておく必要があります。これは通常であれば望ましいやり方ではありませんし、REST framework のビューは実際にはもっと適切な挙動をしますが、今の目的にはこれで十分です。

また、個別のSnippetに対応するViewも用意する必要があります。このViewは、Snippetの取得（retrieve）、更新（update）、削除（delete）に使用されます。

```python:
@csrf_exempt
def snippet_detail(request, pk):
    """
    Retrieve, update or delete a code snippet.
    """
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = SnippetSerializer(snippet)
        return JsonResponse(serializer.data)

    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(snippet, data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data)
        return JsonResponse(serializer.errors, status=400)

    elif request.method == 'DELETE':
        snippet.delete()
        return HttpResponse(status=204)
```

最後に、これらのViewをルーティング(マッピング)する必要があります。
`snippets/urls.py`ファイルを作成してください：

```python:
from django.urls import path
from snippets import views

urlpatterns = [
    path('snippets/', views.snippet_list),
    path('snippets/<int:pk>/', views.snippet_detail),
]
```

また、作成したSnippetアプリのURLを読み込むために、`tutorial/urls.py`ファイルにルートのURL設定を追加する必要があります。

```python:
from django.urls import path, include

urlpatterns = [
    path('', include('snippets.urls')),
]
```

現時点では、いくつかの例外的なケースに適切に対応できていないことに注意してください。例えば、不正なJSONを送信した場合や、Viewが対応していないHTTPメソッドでリクエストされた場合、最終的に「`500` サーバーエラー」のレスポンスが返ってしまいます。それでも、今のところはこれで十分です。

## ✏️補足
ここではまず、今回インポートした下記ライブラリの説明をしたいと思います。
```python:
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
```

JsonResponseはJSON形式のレスポンスを返すためのクラスです。
例えば `return JsonResponse(serializer.data, safe=False)` では、シリアライズされたデータを第１引数に指定して、以下のようなJSON形式で返しています：
```javascript:
{'id': 1, 'title': 'sample_title', 'code': 'foo = "bar"\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}, {...}, ...
```

以下のコードの`JsonResponse()` で第２引数に `safe=False` を指定しているのは、`serializer.data` がリスト型）複数Snippetの一覧）だからです。
`JsonResponse` はデフォルトでは辞書型（dict）のみを許可するため、リストを返したい場合は「意図的にリストを返している」ということを `safe=False` で明示する必要があります。

```python:
from django.http import JsonResponse

def snippet_list(request):
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return JsonResponse(serializer.data, safe=False)
        ...
```

以下のコードの `HttpResponse` は、HTMLやプレーンテキストなどのレスポンスを返すための基本的なクラスです。
以下のように、Snippetが存在しない場合に `404` エラーを返すために使われています（`DELETE`メソッドの場合はSnippetを削除した後に、リクエストが成功して返すべきコンテンツがないことを明示するために使用しています）：
```python:
from django.http import HttpResponse

def snippet_detail(request, pk):
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return HttpResponse(status=404)
        ...
```
本文でも説明がありましたが。`@csrf_exempt` はDjangoのViewにCSRF(クロスサイトリクエストフォージェリ)保護を無効化するためのデコレーターです。このデコレーターを使うことで、そのViewに対してCSRFトークンの検証がスキップされるため、APIや外部からのリクエストを受け入れる場合に便利です。ただし、安全性に注意が必要です。
```python:
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def snippet_list(request):
    if request.method == 'GET':
        ...
    elif request.method == 'POST':
        ...
```

`JSONParser` は、JSON形式のデータをPythonの辞書型に変換するクラスです。ここでは、リクエストの `POST`メソッドなどで送信されたJSONデータを辞書型データとして取り出すために使用しています。
```python:
from rest_framework.parsers import JSONParser

def snippet_list(request):
    if request.method == 'GET':
        ...
    elif request.method == 'POST':
        data = JSONParser().parse(request)
```

また、以下のコードでは、`Snippet`モデルと作成した `SnippetSerializer`クラスを使用し、データのシリアライズやModelとのやり取りを行えるようにしています。

```python:
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
```

- - -
### 最初のWeb APIを試す
これで、Snippetを提供するサンプルサーバーを起動できるようになりました。
シェルを終了しましょう。
```
quit()
```
次にDjangoの開発サーバーを起動しましょう。
```
python manage.py runserver

Validating models...

0 errors found
Django version 5.0, using settings 'tutorial.settings'
Starting Development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
```

別のターミナルウィンドウでサーバーをテストすることができます。

APIのテストには、`curl` や `httpie` を使用できます。`httpie` はPythonで書かれた、使いやすいHTTPクライアントです。それでは、これをインストールしてみましょう。

`httpie` は `pip`を使ってインストールできます：
```
pip install httpie
```

最後に全てのSnippetの一覧を取得してみましょう：
```
http GET http://127.0.0.1:8000/snippets/ --unsorted

HTTP/1.1 200 OK
...
[
    {
        "id": 1,
        "title": "",
        "code": "foo = \"bar\"\n",
        "linenos": false,
        "language": "python",
        "style": "friendly"
    },
    {
        "id": 2,
        "title": "",
        "code": "print(\"hello, world\")\n",
        "linenos": false,
        "language": "python",
        "style": "friendly"
    },
    {
        "id": 3,
        "title": "",
        "code": "print(\"hello, world\")",
        "linenos": false,
        "language": "python",
        "style": "friendly"
    }
]
```
もしくは、IDを指定して特定のSnippetを取得することもできます：
```
http GET http://127.0.0.1:8000/snippets/2/ --unsorted

HTTP/1.1 200 OK
...
{
    "id": 2,
    "title": "",
    "code": "print(\"hello, world\")\n",
    "linenos": false,
    "language": "python",
    "style": "friendly"
}
```

同様に、これらのURLにWebブラウザでアクセスすることで、同じJSONを表示することができます。

## ✏️補足
`httpie` は、APIと簡単にやり取りできるコマンドラインのHTTPクライアントツールになります。
- - -
### 現状の確認
ここまでは順調です。現在、DjangoのForms APIによく似たシリアライゼーションAPIと、いくつかの通常のDjangoのViewができています。

現時点でAPI Viewは、JSONレスポンスを返す以外に特別なことはしていませんし、エラーハンドリングの細かいケースにもまだ改善の余地がありますが、ひとまず動作するWeb APIが完成しています。

[<font color="#d14">チュートリアルのパート2</font>](https://qiita.com/yutty-kov/items/f5c33b15f6a7fdacf4ef)では、ここからどのように改善していけるかを見ていきましょう。