目次
こんにちは。sinyです。
本記事では、Django REST Framework(DRF)を使った簡単なAPIの作成手順をまとめました。
エンドポイントに対して銀行名を与えてGETメソッドを投げると支店名一覧を返すような簡単なREST APIを作成していきます。
環境
今回は以下のバージョンで環境構築しました。
- Django (3.0)
- django-filter (2.2.0)
- djangorestframework (3.10.3)
事前準備
Django REST Frameworkを作成する前の事前準備を行います。
モジュールのインストール
仮想環境を作成後に以下のコマンドでモジュールをインストールします。
pip install django pip install djangorestframework pip install django-filter
プロジェクト、アプリケーションの作成
Django REST Frameworkを作成するためにdjagnoプロジェクトとアプリケーションを作成します。
django-admin startproject bank_api cd bank_api python manage.py startapp appv1
settings.pyの設定
settings.pyにアプリケーションとrest_frameworkを追加します。
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', #add 'appv1.apps.Appv1Config', #add ]
Django REST Frameworkの作成
つづいて、Django REST Frameworkの作成です。
DjangoでDjango REST Frameworkを作成するには最低限以下の3つを定義する必要があります。
- Serializer
- ViewSet
- URL pattern
なお、ケースによりますが通常はモデルの定義も必要になってきます。
モデルの定義(models.py)
今回は以下のような銀行名と支店名コードのテーブルを例にDjango REST Frameworkを作成していきます。
from django.db import models class Bank(models.Model): class Meta: verbose_name ="銀行名" verbose_name_plural = "銀行名" name = models.CharField("銀行名",max_length=255) bank_code = models.CharField("銀行コード",max_length=8) def __str__(self): return self.name class Branch(models.Model): class Meta: verbose_name ="支店名" verbose_name_plural = "支店名" bank_name = models.ForeignKey(Bank, on_delete=models.CASCADE) branch_name = models.CharField("支店名",max_length=255) branch_code = models.CharField("支店コード",max_length=6) def __str__(self): return self.branch_name
Serializerの定義(serializer.py)
Django REST Frameworkにおけるシリアライズ、デシリアライズは以下のような処理を表します。
シリアライズ | JSON文字列などをDjangoモデルオブジェクトに変換すること |
デシリアライズ | モデルオブジェクトからJSON形式などに変換すること |
また、Django REST Frameworkのシリアライザーには大きく以下の3種類があります。
serializerの処理 | 意味 |
---|---|
ModelSerializer | 単一のモデルオブジェクトを利用 |
Serializer | 単一のリソースを扱う、またはモデルに依存しないカスタマイズな処理を実装する |
ListSerializer | 複数リソースを扱う |
今回は、Djangoモデル定義をそのまま活用するのでModelSerializerを使っていきます。
appv1/serializer.pyを作成し、以下のコードを記載します。
from rest_framework import serializers from .models import Bank, Branch class BankSerializer(serializers.ModelSerializer): class Meta: model = Bank fields = ('name', 'bank_code') class BranchSerializer(serializers.ModelSerializer): class Meta: model = Branch fields = ('bank_name', 'branch_name', 'branch_code')
ModelSerializerを使うと非常に簡単にREST APIのシリアライザを定義することができます。
Metaクラス内にmodel属性としてmodels.pyで定義したモデルクラス名を指定します。
そして、fieldsにAPIとして出力したいフィールド名を指定してあげます。
fields属性は以下のように指定可能です。
- リストorタプル形式で指定
- すべてのフィールドを指定する場合は「__all__」
- 除外設定した場合は、exclude = [branch_code']のようにする。
今回は銀行名を扱うBankSerializerシリアライザと銀行の支店名を扱うBranchSerializerの2つを定義しました。
ViewSetの定義(views.py)
続いてビューの定義です。
今回はrest_frameworkのModelViewSetを使います。
querysetにはすべてのレコードを格納するようにします。
serializer_classには利用したいシリアライザクラス(serializer.pyで定義したクラス)を指定します。
import django_filters from rest_framework import viewsets, filters from .models import Bank, Branch from .serializer import BankSerializer, BranchSerializer class BankViewSet(viewsets.ModelViewSet): queryset = Bank.objects.all() serializer_class = BankSerializer class BranchViewSet(viewsets.ModelViewSet): queryset = Branch.objects.all() serializer_class = BranchSerializer
URL pattern定義
次にアプリケーション配下にurls.pyを作成して以下を定義します。
from rest_framework import routers from .views import BankViewSet, BranchViewSet router = routers.DefaultRouter() router.register(r'bank', BankViewSet) router.register(r'branches', BranchViewSet)
ここでは、Django REST Frameworkのroutersというものを使ってModel毎に以下のようなURLパターンを定義しています。
- GET /api/bank/ でBankモデルの一覧を取得
- GET /api/branches/ でBranchモデルの一覧を取得
次にプロジェクト配下のurls.pyで先ほど定義したアプリケーションのurls.pyをインクルードしてあげます。
from django.contrib import admin from django.urls import path, include from appv1.urls import router as bank_api_router #add urlpatterns = [ path('admin/', admin.site.urls), path('api/', include(bank_api_router.urls)), #add ]
テストデータの登録
ここまできたら、一旦マイグレーション、管理者ユーザの作成後にrunserverします。
python manage.py makemigrations python manage.py migrate python manage.py createsuperuser python manage.py runserver
次にadmin画面からテストデータを登録するためにadmin.pyに以下を定義します。
from django.contrib import admin from .models import Bank, Branch class BankAdmin(admin.ModelAdmin): list_display=('pk','name', 'bank_code') class BranchAdmin(admin.ModelAdmin): list_display=('pk','bank_name', 'branch_name', 'branch_code') admin.site.register(Bank, BankAdmin) admin.site.register(Branch, BranchAdmin)
http://127.0.0.1:8000/adminにアクセスして銀行と支店情報をいくつか登録しておきます。
今回は4つの銀行と、三井住友の支店情報を4つだけ登録しておきます。
【銀行情報】
【支店情報】
API動作確認
開発サーバが起動したらhttp://127.0.0.1:8000/api/にアクセスして以下のようなAPIの画面が表示されることを確認します。
次に、銀行名の一覧表示用Rest API(http://127.0.0.1:8000/api/bank/)にアクセスして以下のように銀行名の一覧が表示されればOKです。
PK=1の銀行名のレコード(三井住友)にアクセスするにはhttp://127.0.0.1:8000/api/bank/1/のようにアクセスします。
ClientからJson形式でデータを取得する。
まず、クライアントPCのブラウザからhttp://127.0.0.1:8000/api/bank/?format=jsonのようにアクセスすると、以下のようにJSON形式で銀行名の一覧情報を取得できます。
[{"name":"三井住友","bank_code":"0009"},{"name":"三菱UFJ","bank_code":"0005"},{"name":"三菱UFJ信託","bank_code":"0288"},{"name":"三井住友信託","bank_code":"0294"}]
curlコマンドで取得した場合はcurl http://127.0.0.1:8000/api/bank/?format=jsonを実行します。
※文字化けする場合はchcp 65001を実行
pythonコマンドから取得したい場合は以下のようなコードを実行します。
※以下は銀行名の一覧を抽出する例
>>> import requests >>> import urllib.parse >>> import json >>> API = "http://127.0.0.1:8000/api/bank" >>> values = { ... "format": "json", ... } >>> params = urllib.parse.urlencode(values) >>> url = API + "?" + params >>> req = requests.get(url) >>> data = json.loads(req.text) >>> print(data) [{'name': '三井住友', 'bank_code': '0009'}, {'name': '三菱UFJ', 'bank_code': '0005'}, {'name': '三菱UFJ信託', 'bank_code': '0288'}, {'name': '三井住友信託', 'bank_code': '0294'}] >>> type(data) <class 'list'> >>> bank_list = [item['name'] for item in data] >>> print(bank_list) ['三井住友', '三菱UFJ', '三菱UFJ信託', '三井住友信託'] >>>
カスタマイズ
次に支店情報一覧のAPI(http://127.0.0.1:8000/api/branches/)にアクセスしてみましょう。
以下の通り支店情報の一覧が表示されますが、bank_name(銀行名)の部分が外部テーブル参照になっている関係でPK番号で表示されていてちょっとわかりにくいです。
外部参照キーになっているbank_nameがPKキー表示になっているので、名称表示に変えてみます。
serializer.pyで定義したBranchSerializerクラスに以下のコードを追加します。
class BranchSerializer(serializers.ModelSerializer): bank_name = BankSerializer() #追加 class Meta: model = Branch fields = ('bank_name', 'branch_name', 'branch_code')
先ほどの画面を更新すると以下の通りbank_nameの部分がpk番号→銀行名、銀行コードに変わります。
ペジネーション
Django REST Frameworkで1回のリクエストで取得できる件数を制限し、必要に応じて次ページ分のデータを取得できるようにします。
ページネーションはsettings.pyに以下を追加するだけです。
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 2 }
その後画面を更新すると以下の赤枠のようにページネーション機能が追加されます。
フィルタ機能
次に、Django REST Frameworkにフィルタ機能を追加してみます。
フィルタ機能を利用するにはsettings.pyのINSTALLED_APPに「 'django_filters',」を追加します。
※django_filtersは最初にpipでインストール済み。
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'appv1.apps.Appv1Config', 'django_filters', #add ]
さらにsettings.pyのREST_FRAMEWORKにDEFAULT_FILTER_BACKENDSを以下の通り追加します。
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 2, 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) }
次にviews.pyのBranchViewSetにfilter_fields属性を追加します。
今回は銀行名でフィルタするものとします。
class BranchViewSet(viewsets.ModelViewSet): queryset = Branch.objects.all() serializer_class = BranchSerializer filter_fields = ('bank_name',) #add
画面を更新すると、以下の通りFiltersボタンが追加され、銀行名でフィルタ検索できるようになります。
ちなみにこの時、URLは「http://127.0.0.1:8000/api/branches/?bank_name=1」のようになっています。
通常リクエスト投げるときはbank_name=1という指定方法ではなく、銀行名を渡してリクエストの結果を受け取るという使い方が多いと思います。
先ほど設定したviews.pyのfilter_filedsの設定をbank_name → bank_name__nameに変更します。
これで、「http://127.0.0.1:8000/api/branches/?format=json&bank_name__name=<銀行名>'」という形式でリクエストを投げれるようになります。
class BranchViewSet(viewsets.ModelViewSet): queryset = Branch.objects.all() serializer_class = BranchSerializer filter_fields = ('bank_name__name',) #修正
以下は、pythonコードでGETリクエスト('http://127.0.0.1:8000/api/branches?format=json&bank_name__name=三井住友)を投げる例です。
>>> bank_name="三井住友" >>> API = "http://127.0.0.1:8000/api/branches" >>> values = { ... "format": "json", ... "bank_name__name": bank_name, ... } >>> params = urllib.parse.urlencode(values) >>> url = API + "?" + params >>> url 'http://127.0.0.1:8000/api/branches?format=json&bank_name__name=%E4%B8%89%E4%BA%95%E4%BD%8F%E5%8F%8B' >>> req = requests.get(url) >>> data = json.loads(req.text) >>> data {'count': 4, 'next': 'http://127.0.0.1:8000/api/branches/?bank_name__name=%E4%B8%89%E4%BA%95%E4%BD%8F%E5%8F%8B&format=json&limit=2&offset=2', 'previous': None, 'results': [{'bank_name': {'name': '三井住友', 'bank_code': '0009'}, 'branch_name': '新宿支店', 'branch_code': '221'}, {'bank_name': {'name': '三井住友', 'bank_code': '0009'}, 'branch_name': '新橋支店', 'branch_code': '216'}]}
お勧めの書籍
Django REST Frameworkについては下記リンクの公式チュートリアルがあります(英語)
上記チュートリアルもわかりやすくて勉強になりますが、日本語書籍でDjango REST Frameworkに関するおすすめ書籍を1冊紹介しておきます。
akiyokoさんが執筆販売している書籍ですが、導入部では初心者の方にもわかりやすくDRFの概念やクラスの種類などが紹介されています。
かなり細部にわたってDjango REST Frameworkの仕組みを解説しているため、今後DRFを活用していきたい方は是非読んでおくことをお勧めします。
以上、「Django REST Frameworkで簡単なREST APIを実装する方法」でした。