"BOKU"のITな日常

BOKUが勉強したり、考えたことを頭の整理を兼ねてまとめてます。

Django REST Framework (DRF)のAPIView・api_viewを使う/python・django

f:id:arakan_no_boku:20190607195913j:plain

目次

Django REST Framework (DRF

Django REST Framework (DRF)はDjangoでWeb-APIを開発するフレームワークです。

RESTの意味

DRFの「REST」は「REpresentational State Transfer」の略です。

HTTPメソッドでアクセスしてデータの送受信を行うのを、RESTなWebサービスと呼びます。

RESTの設計原則がRESTfulです。

おおよそ、以下の4つの原則に従うのをRESTful なWebサービスと呼びます。

  • アドレス指定が可能なURLで公開されている
  • インターフェースがHTTPメソッドの利用に統一されている
  • リソースをHTMLやテキスト、XMLJSONなど多様な形式で表現できる
  • ステートレスな通信

もう少し詳しい話は、このあたりの記事がわかりやすいと思ったので、リンク貼っておきます。

qiita.com

DRFDjangoの違い

DRFDjangoの最大の違いはレスポンスの返し方です。

Djangoは「HTTPResponse」を返し、DRFは「Response」を返します。

Djangoは「HTML」を返し、DRFでは「JSON等」を返します。

Viewも違います。

DRFのViewは以下の観点で大きく2つにわかれます。

  • 特定のモデル(DB・クエリセット)に紐づくView
  • 特定のモデル(DB・クエリセット)に紐づかないView

です。 

特定のモデル(DB・クエリセット)に紐づくビュー

Generic viewとViewSetsです。

 作成, 一覧, 取得, 削除, 更新 などのお作法に従って使う場合、可能な限り簡略化して書けるようになっています。

DjangoのFormのAPI版みたいなクラスでと関連づけるだけで、処理を書かなくても、ちゃんと機能するAPIが書けたりする、とても「FrameWork」っぽいViewです。

Generic viewはやりたい処理に対応する多数のクラスの総称みたいな感じです。

ViewSetsは、 取得, 一覧, 登録, 更新, 削除 などのCRUD操作をまとめて扱えるViewです。

ModelViewSetsを継承すると、GenericViewと同様にSerializer を定義するだけで、 取得, 一覧, 登録, 更新, 削除ができるAPIが書けるので、間違いなく便利ですね。

それに、ネットでDRFの情報を探すと、大体ViewSetsを使う例がでてくるので、サンプルにもこまらないのかなと思います。

Generic view

www.django-rest-framework.org

ViewSets

www.django-rest-framework.org

 

特定のモデル(DB・クエリセット)に紐づかないビュー

GenericViewもViewSetsも便利ですけど、DBを使わない場合には使えません。

そういう場合は「APIView」か「api_view」を使います。 

www.django-rest-framework.org

APIViewが「クラスベースのView」

api_viewが「関数型のView」です。

今回とりあげるのは、こちらです。

この両方を使って同じ処理を書いて、比較もしてみます。

サンプルでWebAPIプロキシを作成する

プロキシは英語で「proxy」と書きます。

日本語だと「代理」「中継」みたいな意味です。

WebAPIプロキシは文字通り、既存のWeb-APIを呼び出して結果を中継して返します。

APIは、郵便番号取得APIを使います。

zipcloud.ibsnet.co.jp

関数型ビューでの実装サンプル

まずは、ソースコードです。

rest/views.py

import requests
from rest_framework.decorators import api_view
from rest_framework.response import Response


@api_view(['GET', 'POST'])
def get_address_from_zip(request):
    zipcode = request.GET.get(key="zipcode", default="1000011")
    resp = requests.get(
        'http://zipcloud.ibsnet.co.jp/api/search?zipcode=' +
        str(zipcode) +
        '&limit=1')
    return Response(resp.json(), status=resp.status_code)
 

補足します。

アノテーション 

@api_view(['GET', 'POST'])

がついて、Responseで返している以外は、普通のメソッドです。

間違いやすいとしたら、 

Response(resp.json())

ではなくて

 Response(resp.json(), status=resp.status_code)

のように、statusを引き継ぐように書いている部分だと思います。

前者でも何となく動くんですけど、元のAPIのStatusが隠れてしまったりするので後者の方がいいみたいです。

クラスベースのViewでの実装サンプル

今回は「 AddressFromZip」クラスを「addressFromZip.py」として作ります。

まずは、ソースコードから。

rest/address_from_zip.py

import requests
from rest_framework.views import APIView
from rest_framework.response import Response


class AddressFromZip(APIView):
    def get(self, request):
        zipcode = request.GET.get(key="zipcode", default="1000011")
        resp = requests.get(
            'http://zipcloud.ibsnet.co.jp/api/search?zipcode=' +
            str(zipcode) +
            '&limit=1')
        return Response(resp.json(), status=resp.status_code)

補足します。 

クラスベースのViewは「APIView」を継承して、メソッドの「get」「post」をオーバーライドする形になります。

上記例では、getだけオーバーライドしてますけど、getとpostの両方をオーバーライドしてもかまいません。

この名前を任意のものに変えると、動きません。

要注意です。

WebApiのURLネーミング規則 

WebApiプロキシといっても、一応利用する側から見たらAPIですから、URLのネーミングについては、一般的な規則にそってつけることにします。

この辺が参考になります。

cloud.google.com

qiita.com

urls.pyでパスを設定し実行する

ソースです。

rest/urls.py 

from django.contrib import admin
from django.urls import path
from . import views
from . import adress_from_zip as zip

urlpatterns = [
    path(
        'admin/',
        admin.site.urls),
    path(
        'api/v1/addresses/zip/get',
        zip.AddressFromZip.as_view(),
        name='cls-v'),
    path(
        'api/v1/addresses/zip',
        views.get_address_from_zip,
        name='fnc-v'),
]

補足します。 

URLの構成は、「api/v1/addresses/zip」を基本にしています。

「v1」がバーション。

「addresses」は取得するのが住所(アドレス)なので、その複数形。

「zip」はzipコードから取得するという意味。

原則、名詞しか使わないという基準に基づいて決めてます。

クラスベースの場合の指定サンプル

以下のように指定してます。

'api/v1/addresses/zip/get',
adressFromZip.AddressFromZip.as_view(),
name='cls-v'),

クラスベースの場合、get・postのオーバーライドした方をURLの最後につけて、クラス名.as_view() で定義します。

as_view()によって、URLのgetにマッピングされる・・という仕組みたいですね。

関数ベースの場合の指定サンプル

以下のように指定しています。

'api/v1/addresses/zip',
views.getAddressFromZip,
name='fnc-v'),

Djangoの普通のViewの指定に似ています。

関数名を渡して、任意のURLとマッピングしているだけですから。

実行イメージ

プロジェクトフォルダをカレントフォルダにして、djangoをインストールした仮想環境をactiveにします。

それでお馴染み「python manage.py runserver」します。

郵便番号はパラメータで渡すので、こんな感じ。

ちなみに郵便番号は「1000013」にしてみました。

クラスベースの場合

http://localhost:8000/api/v1/addresses/zip/get?zipcode=1000013

関数ベースの場合

http://localhost:8000/api/v1/addresses/zip?zipcode=1000013

URLをたたいてみると

f:id:arakan_no_boku:20190615175823p:plain

ちゃんと、とれてます。

今回は、こんなところで・・。

ではでは。