DjangoRESTFrameworkのAPIView/Generic(汎用)APIView/ModelViewSetは結局どれを使うといいのか

# 目的
DjangoRESTFrameworkについてある程度学んだ後、よしプロジェクトに加わろうと思ったら、一番実装がシンプルで簡単だと知ったModelViewSetやGeneric(汎用)APIViewは全く使われておらず、APIViewしか使われていなかったため、それぞれの特徴をしっかりと掴むべきだと考えたため。

# 前提
python, django, django_restframeworkについてある程度の知識はあるもののユースケースがわからなかったり、社内の大規模システムなどで実際に利用したことがなくイメージが付きづらい人向け

DjangoRESTFrameworkではdjangoと同様にurls.pyでフロントエンドからのリクエストをそれぞれのViewにルーティングするが、**そのクラスベースViewには①APIView②Generic(汎用)APIView③ModelViewSetの三種類が存在する。**
この３種類のユースケースについて学んでみた。


# 概要
シンプル簡単な順に並べると
**ModelViewSet > GenericView > APIView**
細かなカスタマイズができる順に並べると逆に
**ModelViewSet < GenericView < APIView**
こうなる。

**ModelViewSet**: 単一モデルのCRUD(create, read, update, deleteメソッド)を処理するRESTAPIを最速で少ないコードで実行したい場合はまずModelViewSetを利用する

**Generic(汎用)APIView**: そうでない場合で対象が単一モデルの場合は汎用APIViewを利用する

**APIView**: モデルを扱わない/モデルを複数扱う場合などで上記の二つで対応ができないような場合はAPIViewを使うことになる

# 詳細
### ModelViewSet
ModelViewSetを利用すると、基本的には下記のようにquerysetに対象となるモデルをセット、serializer_classに利用するserializerを利用するだけで、GET/POST?PUT/DELETE/PATCHが利用できるようになる。

```views.py
class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
```
しかし下記の例のように、ただモデルからgetしたものをそのまま返すのではなく、カスタマイズしてレスポンスしたい場合などは@actionを追加して各メソッドをオーバライド(上書き)することができる。

```views.py
class MemberViewSet(viewsets.ModelViewSet):
    queryset = Member.objects.all()
    serializer_class = MemberSerializer
   
    @action(methods=['get'], detail=False)
    def respect_call(self, request):
        for member in self.get_queryset()
        return Response([
            '{member.username}さん'.format(member=member)

        ])

    @action(methods=['get'], detail=True)
    def respect_call(self, request, pk=None):
        member = self.get_object()
        return Response('{member.username}さん'.format(member=member))
```
**この部分では、methodsにオーバーライドしたいHTTPメソッドを指定し、詳細のAPIを利用する場合はdetailをTrue、関数の引数にpkを追加する。一覧のAPIを利用する場合はFalseとしpkは不要。**

```views.py
@action(methods=['get'], detail=False)
```
例えば
getメソッドで特定のデータを取得する場合はpkを利用して、特定の詳細のデータを取得するため、detailをTrue、関数の引数にpkを追加する。
逆にgetメソッドで一覧を取得する場合はもちろんpkも要らず詳細データではないので、detailはFalse,引数にpkを加える必要はない。

### Generic(汎用)APIView
GenericAPIViewはModelViewSetに似ていて、querysetとserializerをセットするとGenericAPIViewがよしなにしてくれる。
**だが、ListAPIView、CreateAPIView,ListCreateAPIViewなど実装するHTTPメソッドごとに実装しなくてはいけないことはModelViewSetと大きく異なる点。**

以下のコードで取得APIの最低限の実装はできる。

```python
class UserList(generics.ListAPIView):
    queryset = Member.objects.all()
    serializer_class = MemberSerializer
```
しかしカスタマイズが必要な場合はやはり関数をオーバーライドして下記のように書く必要がある。

```python
class UserList(generics.ListCreateAPIView):
    queryset = Member.objects.all()
    serializer_class = MemberSerializer

    def list(self, request):
        queryset = self.get_queryset()
        serializer = MemberSerializer(queryset, many=True)
        return Response(serializer.data)
```


### APIView
下記のコードのように各HTTPメソッドを全て書かないといけない。
今回のコードで言えばgetメソッドの実装を書く必要があり、その際はrequest.dataをデシリアライズし、データの取得をし、レスポンスのシリアライズまで全て書いている。

```views.py
class SpecificMemberAPIView(views.APIView):
    def get(self, request, member_id):
      # 後にtokenなどが入る可能性もあるため配列にしておく
      request_data = request.data
      request_data['member_id'] = member_id
      request_serializer = SpecificMemberRequestSerializer(data=request_data)
      # requestのバリデーションを実行
      if request_serializer.is_valid():
        # SpecificMemberRequestSerializer内createメソッド実行
        response_data = request_serializer.save()
        # requestだけではなくresponseもバリデーションを行う
        response_serializer = SpecificMemberResponseSerializer(response_data)
        return Response(
          # レスポンスされるデータはserializerのdataに格納される
          response_serializer.data
        )
```


# 参考文献
現場で使えるDjangoRESTFrameworkの教科書
[[Django REST Framework] View の使い方をまとめてみた](https://note.crohaco.net/2018/django-rest-framework-view/)