Django REST Frameworkで簡単なREST APIを実装する方法

スポンサードリンク



こんにちは。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.pyREST_FRAMEWORKDEFAULT_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を実装する方法」でした。

おすすめの記事