Django REST FRAMEWORK Tutorial 10 -- def snippet_list を Class SnippetList に書き直す
Tags: django
Published: 2022-05-08 15:14:17


## 何が変わるのか

https://www.django-rest-framework.org/tutorial/3-class-based-views/

```py
@api_view(['GET', 'POST'])
def snippet_list(request, format=None):
    if request.method == 'GET':
    elif request.method == 'POST':
```

`@api_view` の後に def で snippet_list で関数を作って
request.method によって if で分岐して処理していた。

```py
class SnippetList(APIView):
    def get(self, request, format=None):
    def post(self, request, format=None):
```

これを SnippetList という class を作って
get, post のメソッドごとに 関数を作る構成にする

---

こうすることで、全体を指すものはクラス、各自メソッドを指すものは関数
と分けることができるのでコードが見やすくなると推測する。

また、get_object を関数にして各メソッドから呼び出すようにすることで
コードの可読性が良くなると推測する。

---
---

# SnippetList のコードを書いていく

## api_view の書き換えと Class SnippetList の全体像

```py
from rest_framework.decorators import api_view
```

今まで関数の前に使っていた api_view から

```py
from rest_framework.views import APIView
```

クラスの引数として用いる APIView に変更

```py
class SnippetList(APIView):
    def get(self, request, format=None):
    def post(self, request, format=None):
```

こうして class と def で書き直す。
中身はのロジックは以前 if で分岐していた時と全く同じ。

---
---

# snippet_detail を書き直す

こちらは新たに作成される get_object が依然と大きく異なる。

## 全体構成

```py
class SnippetDetail(APIView):
    def get_object(self, pk):
    def get(self, request, pk, format=None):
    def put(self, request, pk, format=None):
    def delete(self, request, pk, format=None):
```

このように、SnippetDetail クラスの中に
関数を作っていく。

---

## get_object の用途

以前は def snippet_detail 直下で

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

最初に try で snippet 変数に取ってきておいて

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

各メソッドでは、その snippet を使うようにしていた。

これを今回は

```py
    def get_object(self, pk):
        try:
            return Snippet.objects.get(pk=pk)
        except Snippet.DoesNotExist:
            raise Http404
```

オブジェクトを取ってくる一つの関数 get_object に切り出して

```py
    def get(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)
```

各メソッド関数で self から self.get_object として 使用する。
JavaScript の this のようなものだと解釈する。

---

## get_object を実際に書く

```py
from django.http import Http404
```

Http404 を使うので import して

```py
    def get_object(self, pk):
        try:
            return Snippet.objects.get(pk=pk)
        except Snippet.DoesNotExist:
            raise Http404
```

get_object では pk に応じてオブジェクトを取得し
なければ Http404 を返す。
別の関数に切り出し Http404 を使う以外は前回と変更点はない。

---

## get, put, delete

```py
    def get(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        snippet = self.get_object(pk)
        snippet.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
```

全てのメソッド関数で self から get_object を使って
現在の primary key のオブジェクトを取ってくるようにする。

---

## urls に クラス名を適用して .as_view() もつける。

snippets/urls.py を見ると

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

以前は views. snippet_list になっていたが

class と APIView を使うとこれでは読み込めない。

```py
    path('snippets/', views.SnippetList.as_view()),
    path('snippets/<int:pk>/', views.SnippetDetail.as_view()),
```

views. の後に SnippetList, SnippetDetail と
CamelCase に直したクラス名を記述して
.as_view() をつける。

これでクラスベースで読み込まれるようになった。