Django REST FRAMEWORK Tutorial 8 -- snippet_detail の GET/PUT/DELTE を csrf_exempt で作成する
Tags: django
Published: 2022-04-20 17:15:44


## 何をやるのか

前々回の記事と前回の記事で、snippet_list の一覧の API を CSRF_EXEMPT と API_VIEW で GET/POST できるようにした。

今回は snippet_detail の詳細の API を CSRF検証を無効化して作成する

---

## snippets/urls に snippet_detail のルーティングを追加

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

snippets/1 や snippets/99 のパスにアクセスした時に見るところを
views の snippet_detail に設定する

---

## snippets/views に snippet_detail を記載

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

モデルからのオブジェクトの取り出し、
GET/PUT/DELETE の JSON レスポンスへの処理
これらを書いている
一つ一つ見ていく

---

## CSRF 検証無効化と request と pk

```py
@csrf_exempt
def snippet_detail(request, pk):
```

最初に CSRF 検証無視を記載
request と　pk を受け取る
primary key, つまり id

---

## Snippet モデルからオブジェクトを取得、例外処理

```py
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return HttpResponse(status=404)
```

受け取った pk からデータを取り、変数 snippet に入れる
それがなくて失敗したら、404 を返す

## GET

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

GET の時は普通にモデルから取り出したデータを
シリアライズして JSON レスポンスで返す

---

## PUT

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

PUT の時は request の中身を JSON パーサーにかけて
シリアライザにかけて、中身があれば JSON レスポンスで返す。

---

## DELETE

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

DELETE の時は単に取ってきたオブジェクトに .delete() をかけて
レスポンスの 404　を返している

POST は存在しない。
snippet_detail では既にあるものにアクセスしてるからだ。

---

## Postman で動作確認

http://localhost:8002/snippets/2/

に Postman で GET アクセスすると

> "GET /snippets/2/ HTTP/1.1" 200 120

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

id 2 の データが無事に帰ってきた

次に同じ URL に PUT で HTTP Body に
title を Detail PUT Test に変えた　JSON を渡すと

>  "PUT /snippets/2/ HTTP/1.1" 200 128

```json
{
    "id": 2,
    "title": "Detail PUT Test",
    "code": "print(\"updated\")",
    "linenos": false,
    "language": "python",
    "style": "friendly"
}
```

ちゃんと update されたものが返ってきた

最後に `/` をつけないと
500 internal server error が出るので注意

また、DELETE を送ると

> "DELETE /snippets/2/ HTTP/1.1" 204 0

無事に削除された。



---

## まとめ

snippet_detail では
Snippet モデルから渡された id(pk) のデータをとって
なかった場合は 404 を返して
GET の場合はシリアライズして JSON で返す
PUT の場合は JSON パーサーしてシリアライズに成功すれば保存
失敗すれば 400 でエラーを返す
DELETE の場合は delete() をかけて 204 を返す

これで詳細ページの API が作れた