DjangoのRest Frameworkについて学んでいます。
学んだことをアウトプットするため、備忘録を書いていきます。
今回はViewに焦点を当てました。
間違った点がありましたら、教えていただけますと幸いです。
関数ベースのView(一番簡単)
Responseを返却する関数型のビューです。
第一仮引数に request を受取るように定義するだけです。
from rest_framework.response import Response
from rest_framework.decorators import api_view # ここにapi_viewというデコレータを記述することにより、DRFの設定が、下記ビューに引き継がれるようになります。
@api_view(['GET', 'POST']) # 引数としてHTTPメソッドを指定
def hello_world(request):
if request.method == 'POST':
return Response({"message": "Got some data!", "data": request.data})
return Response({"message": "Hello, world!"})
単純な機能は、上記のように関数ベースで書いたほうが可読性がよいですが、設定項目が増えてきたら見栄えが悪くなるため、後述するクラスベースにしたほうが良い。
クラスベースのビュー
クラスベースのビューは、HTTPメソッドごとのインスタンスメソッドを定義できます。
また、他にも後述する様々な設定を記述することができます。
関数ベースだとデコレータで連ねて書く必要があり可読性が悪くなってしまうため、
クラスベースのビューのメリットであると言えます。
クラスベースのビューは、APIViewクラスを継承して定義します。
対象のHTTPメソッドがそのままインスタンスメソッドになります(DRF的に呼ぶと"ハンドラメソッド")。
# APIViewクラスを継承
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import authentication, permissions
from django.contrib.auth.models import User
# APIViewクラスを継承
class UserNameAPI(APIView):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAdminUser,)
# ↓対象のHTTPメソッドがそのままハンドラメソッドになっている
def get(self, request, format=None):
usernames = [user.username for user in User.objects.all()]
return Response(usernames)
# ↓対象のHTTPメソッドがそのままハンドラメソッドになっている
def post(self, request):
# 普通こんなことはしないが...
users = [User(username=name) for name in request.POST.getlist('name')]
User.objects.bulk_create(users)
return Response({'succeeded': True})
このように、モデルに紐付かない処理はクラスベースのビューを記述するのが適切です。
ちなみに後述のジェネリックビュークラスは、APIViewを継承して定義されています。
ジェネリックビュー
DjangoやDRFのお作法に従うなら、ジェネリックビューがおすすめ。便利で種類も多いです。
ジェネリックビューはモデル(クエリセット)に紐づくため、簡略化した書き方ができるのが特徴です(処理を書かなくてよいことも多々あり)。
種類がとても多いように感じそうですが、作成, 一覧, 取得, 削除, 更新 の用途によって使い分ければいいだけの話なので、身構えることはありません。
手始めにListAPIViewを使ってみましょう。
from rest_framework import generics
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
たったこれだけでユーザ一覧を返却するAPIビューの出来上がりです。 各ユーザレコードは UserSerializer に従ってシリアライズされます。
ビューセット
ビューセットは 取得, 一覧, 登録, 更新, 削除 をまとめて管理するビューです。
自分で書かなくてもこのViewSetが肩代わりしてくれるので楽ですね。
from rest_framework import viewsets
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
たったこれだけで、取得, 一覧, 登録, 更新, 削除 ができるようになりました。
なお、ViewSet は ハンドラメソッドではなく アクションメソッドで定義するべきとの記述を読みました(それに対してジェネリックビューはハンドラメソッド)。
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
pagination_class = MyCursorPagination
@action(methods=['get'], detail=False)
def fullnames(self, request):
return Response([
'{user.first_name} {user.last_name}'.format(user=user)
for user in self.get_queryset()
])
@action(methods=['get'], detail=True)
def fullname(self, request, pk=None):
user = self.get_object()
return Response('{user.first_name} {user.last_name}'.format(user=user))
# Router については後述
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = [url(r'^api/', include(router.urls))]
下記のように、インスタンスメソッド名でURLにアクセスすることで任意の処理が実行できます。
$ curl -X GET 'http://127.0.0.1:8000/api/users/fullnames/'
["takayuki shimizukawa","takanory suzuki","teruhiko teruya"]
$ curl -X GET 'http://127.0.0.1:8000/api/users/1/fullname/'
"takayuki shimizukawa"
$ curl -X GET 'http://127.0.0.1:8000/api/users/2/fullname/'
"takanory suzuki"
$ curl -X GET 'http://127.0.0.1:8000/api/users/3/fullname/'
"teruhiko teruya"
- methods は どの HTTP メソッド を走らせたいのかどうか
- detailの True or False は 一覧APIを取得するのか、詳細API を取得するのか
- detailがTrueの場合は、詳細APIを取得することになるので、pk引数が必要。
個人的に考えた使い分け
- クエリセットを使わない API は クラスベースビュー(APIView) か 関数ビュー
- モデルに対する処理をまとめて一つのクラスで定義したい場合は View set
- それ以外は Generic view
ビューに関する補足情報
ビューは、ルーティングの紐付けをしないとページとして認識されません。
そのためurls.py の urlpatterns 変数に登録します。
from django.conf.urls import url, include
urlpatterns = [
url(r'^hello/$', hello_world),
url(r'^user/$', UserList.as_view()),
]
- 関数のビュー: そのまま登録
- クラスのビュー: as_views() メソッドの結果を url 関数でマッピングすることで登録
- ビューセット: Routerを使って登録
Router
Routerというものを使って、urls.pyにルーティングルールを追加していきます。
やること自体は簡単なんですが、残念なことにいくつかの種類があります。。。
ここでは DefaultRouter だけ説明します。 SimpleRouter は DefaultRouter と大体同じものです。
ですが DefaultRouter は Router のルート画面にアクセスしたときに API のリンク一覧を見せてくれるので、SimpleRouterより少しだけ便利です。
じゃあブラウザから直接APIを見たいときはどうするのかと言いますと、
.json の拡張子をつけたり、クエリストリングとして ?format=json をつけたりして、アクセスができます。
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = [
url(r'^api/', include(router.urls)),
]
これにより /api/ 配下にビューが登録されます。 The Browsable API (私の場合は http://localhost:8080/api/) にアクセスすると、上記 register で登録されたビューをたどることができるようになります。
(例えば、 http://localhost:8080/api/users/ )
補足情報
-
Routerで登録できるのは、ビューセットだけです。ジェネリックビューなど他の種類のビューを登録しようとすると、
AttributeError: type object 'UserListView' has no attribute 'get_extra_actions' のようにエラーが発生します。 get_extra_actions メソッドは viewsets.py にしか定義されていません。 -
Router は 詳細API を users/{pk}/$ のように pk を自動的に付加してURL登録してくれるいい子です。
request オブジェクト
クライアントからのrequest オブジェクトは、ビューから参照することができます。
簡単ですが、今回はここまでにします。
▼参考にさせていただいた記事
https://note.crohaco.net/2018/django-rest-framework-view/