blog.takurinton.dev

DRFのViewについてまとめる

2020-11-27

こんにちは

どうも、こんにちは、僕です。

今日はDRFのなんちゃらAPIViewについてまとめたいと思います。

自分の推しはAPIViewです。対戦お願いします!

DRFとは?

そんなもんは自分で調べてください(辛辣)

参考までにこれは[前回の記事](https://www.takurinton.com/post/32)でさらっと書いてしまったAPIView周りの深堀りみたいなイメージで描いていきます。

今回使うモデル

今回使うモデルはとてもシンプルで、以下のようなものを想定しています。

  • User
  • name (strint, primary)
  • age (int)
  • sex (int)
  • Userを格納するためのモデルで、nameには名前、ageには年齢、sexには性別が入るようになっています。

    種類

    DRFのViewには様々な種類があります。

    また、関数で実装する場合やクラスで実装する場合などがあります。

    大大大大大前提

    ここで少し前提として知っておくと嬉しいことについてメモ程度に書いておきます。

  • 戻り値はResponse型
  • 通常のDjangoではHttpResponse型でしたが、DRFではResponse型です
  • from rest_framework.response import Response でimportできます
  • やりとりはJSON
  • 当たり前
  • シリアライザーはあってもなくてもいい
  • 他の記事を見てるとシリアライザーを使用している記事が多く、ドキュメントでも使用しているので使いたくなりますが自分は余計なファイルを増やしたりしたくない時(小さいサービスの場合)はめんどくさいので使いません
  • Djangoがせっかく準備してくれてるんだしという心優しい人は積極的に使用してみてください
  • 今度シリアライザー使った場合と使ってない場合の速さ計測したいですね
  • 関数ベースで使う場合

    関数ベースでViewを使用する時にはデコレータを使用してAllow methodを指定してその下にリクエストが来た時の関数を定義してあげます。これをしないとみんな大好き405エラーが返ってきてお陀仏です。

    実際の使い方は以下のようになっています。

    from rest_framework.response import Response
    from rest_framework.decorators import api_view
    
    @api_view(['GET', 'POST'])
    def user(request):
        if request.method == 'GET':
            user = User.objects.filter(is_active=True)
            res = {'message': 'ok', 'user': user}
            return Response(res, status=status. HTTP_200_OK)
        else:
            data = request.data
            user = User(name=data['name'], age=data['age'], sex=data['sex'])
            user.save()
            res = {'message': 'ok'}
            return Response(res, status=status. HTTP_201_CREATED)
    

    こんな感じです。

    処理自体は簡単で、get requestがきたらユーザー全取得、post requestがきたらユーザー登録をするようになっています。

    クラスベースで使う場合

    クラスベースでDRFのViewを使用する場合は大きく分けて2つに分類されます。

    2つのいずれもクラスを継承して実装することになります。

    データベースに紐づくやつ

    これはシリアライザーを作成していい感じにしていく手法です。

    まあ公式がこれ継承して使えばええで!って言ってるので使います。

    これがまた書きやすいんですよね、可読性とかコードの追いやすさは置いておいて、とても簡単にデータベースから値を出すことができます。

    また、これを使うときにはGeneric Viewというものを使用します。これには複数の種類があります。

  • CreateAPIView
  • post request用
  • 作成専用
  • ListAPIView
  • get request用
  • 一覧取得、イテラブルな感じのレスポンスになる
  • 読み取り専用
  • RetrieveAPIView
  • get request用
  • 唯一に識別できるやつを取得する
  • 主キーで指定する場合
  • DestroyAPIVIew
  • delete request用
  • 削除専用
  • RetrieveAPIViewと同じで1つの要素だけを扱う
  • UpdateAPIView
  • put || patch request用
  • 更新専用
  • RetrieveAPIViewと同じで1つの要素だけを扱う
  • これらのメソッドがあり、それぞれの特性に合わせて継承して扱います。

    さらにこいつらは組み合わせて使用することができます。詳しくは[こちら](https://www.django-rest-framework.org/api-guide/generic-views)を見てみてください。

    また、シリアライザーとしてDBのオブジェクトを渡してあげるとよしなにやってくれます。

    例えばこんな感じ

    # serialyzer.py
    from rest_framework import serializers
    
    from .models import User
    
    # シリアライズの対象を指定する
    class UserSerializer(serializers.ModelSerializer):
        class Meta:
            model = User # 取得したいモデル
            fields = '__all__' # フィールドをタプル、またはリストで渡す。全部欲しい時は__all__で取得できる
    
    

    このようにしてシリアライザーを定義します。ここでMetaクラスの中に取得したいモデルとフィールドを指定することでそのオブジェクトと指定したフィールドの値を取得することができます。

    # views.py
    from .serializer import UserSerializer
    
    class UserView(generics.ListAPIView):
        serializer_class = UserSerializer # 先ほど指定したシリアライザーを指定
        queryset = User.objects.filter(is_active=True) # クエリを指定
    

    今回は一覧取得がしたかったのでListAPIViewを使用しました。

    ここでserializerという変数に先ほど作成したシリアライザーを渡すことで先ほど指定したモデルのオブジェクトを取得してくれます。

    とても短いコードになりました。これは強い。

    ですが個人的にはこれは汎用性に欠ける気がしていてそこまで好きではありません。ちなみにquerysetに生のSQLを渡すこともできますが好きくない。

    データベースに紐づかないやつ

    圧倒的にこっち派です。

    このやり方はAPIViewを継承して実装をします。

    from rest_framework.response import Response
    from rest_framework.views import APIView
    
    class UserView(APIView):
        def get(self, request):
            user = User.objects.filter(is_active=True)
            res = {'message': 'ok', 'user': user}
            return Response(res, status=status. HTTP_200_OK)
        def post(self, request):
            data = request.data
            user = User(name=data['name'], age=data['age'], sex=data['sex'])
            user.save()
            res = {'message': 'ok'}
            return Response(res, status=status. HTTP_201_CREATED)
    

    ほうらわかりやすい!直感的い!シリアライザー使わなくていいのでとっても嬉しいのです。今回は生のSQL書いてないので影響はなさそうですが、今後書くってなったときに一部ではシリアライザー、一部ではAPIViewを使用して実装してるとわかりにくいのでこうして統一させたい感はあります。

    また、N+1問題をいい感じにやっつけるためにも生のSQLは大事になってくるので積極的に利用していきたいです。

    まとめ

    シリアライザーは最初はわかりにくい部分とかも多いのですが、わかれば使いやすくて便利だなと感じています。

    これ外部キーとかの取得どうするんだろう?とかもし外部キー取得できるならどういうクエリが発行されるんだろうかとかとても気になることがたくさんあるので調べてまた記事にしたいと思います。

    Django、便利ですね、またね。