スクリプトのお勉強 技術

Python Django REST Framework(REST API)の作成

投稿日:

1.はじめに

今回は、pythonの「Django」というWebアプリケーションフレームワークを使用し、REST APIを作成します。
作成するREST APIは、これから作るであろう、Nuxt.jsからアクセスする際に使用するつもりです。

REST APIをなるべく簡単に作成することが目的です。Djangoそのものにはあまり深入りしません。

1.1 Django REST Frameworkとは

APIは、Djangoの[Django REST Framework]を使用します。

Django、及びDjango REST Frameworkを仕事で使用している関係で、少しだけ理解しているので、時間短縮のために使用しました。

ただし、他のライブラリと比較すると少し自由度が低いと思います。「型」として覚えてしまえばいいのかもしれません。

1.2 ソースコードについて

ここに今回実装した、ソースコード一式格納してあります。

2.環境設定

2.1 python環境設定

以下のように環境設定します。

$ mkdir addr_api ; cd addr_api
$ export PIPENV_VENV_IN_PROJECT=true
$ pipenv install django djangorestframework django-cors-headers django-filter

バージョンは以下の通りです。

  • Django==2.2.4
  • djangorestframework==3.10.2

2.2 Django REST Framework設定

ここでは、table_rest_apiというプロジェクトの中に、tableappというREST APIアプリケーションを作成します。

$ pipenv run django-admin startproject table_rest_api
$ cd table_rest_api
$ pipenv run django-admin startapp tableapp

この時点で以下のディレクトリ構成になっています。

./
manage.py
table_rest_api/
    __init__.py
    settings.py
    urls.py
    wsgi.py
tableapp/
    __init__.py
    admin.py
    apps.py
    migrations/
       __init__.py
    models.py
    tests.py
    views.py

2.3 settings.py

settings.pyに以下を追加します。

主にAPI用に追加していますが、LANGUAGE_CODEとTIME_ZONEは、admin画面(後述)用に追加しています。

table_rest_api/settings.py

ALLOWED_HOSTS = ['*']

INSTALLED_APPS = [
    ...(略)
    'tableapp',
    'rest_framework',
]

## (中略)

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

3.REST API作成

以下のようにREST APIを作成していきます。

3.1 モデル定義

モデルを定義します。nuxt.jsのサンプルが住所録的なものなので、Addrsとして定義しました。

見た通りですが、jobsの所だけ妥協しました。本来なら配列を使用するところです。少々面倒なので、今回は文字列にしています。

tableapp/models.py

from django.db import models


class Addr(models.Model):
    GENDER_MAN = "男性"
    GENDER_WOMAN = "女性"
    GENDER_OTHER = "その他"
    GENDER_SET = (
            (0, GENDER_MAN),
            (1, GENDER_WOMAN),
            (2, GENDER_OTHER),
            )

    name = models.CharField(max_length=128)
    birthday = models.DateField()
    gender = models.IntegerField(choices=GENDER_SET)
    address = models.CharField(max_length=128)
    jobs = models.CharField(max_length=1024)
    note = models.CharField(max_length=128)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

3.2 データベース構築

データベース情報を設定します。test_rest_api/settings.pyに既に設定されていますが、
ここでは、SQLite3を使用します。

$ pipenv run python manage.py makemigrations
$ pipenv run python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, tableapp
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying sessions.0001_initial... OK
  Applying tableapp.0001_initial... OK

3.3 admin画面の作成

admin画面を作成します。APIには直接関係ないですが、設定しておくと画面からデータを設定できます。

以下は、管理者ユーザの作成です。

$ pipenv run python manage.py createsuperuser
Username (leave blank to use 'tanino'):
Email address: tanino@a2.mbn.or.jp
Password:
Password (again):
Superuser created successfully.

モデルを管理画面から変更できるようにしておきます。
tableapp/admin.py

from django.contrib import admin

from .models import Addr


@admin.register(Addr)
class AddrAdmin(admin.ModelAdmin):
        pass

以下で起動します。

$ pipenv run python manage.py runserver 0.0.0.0:18899

以下でadmin画面にアクセスできます。
http://192.168.132.129:18899/admin

ログイン画面が以下です。

ログインすると以下の画面になります。

データを追加しておきます。

4. Django REST Framework追加

Django REST Frameworkを追加していきます。

4.1 Serializers追加

Serializersとは、データ入出力時に使用する、モデルへの橋渡しをするクラスです。Formクラスと似ています。
ここでは、AddrSerializersを追加します。Addrモデルとの橋渡しです。fieldsには、APIとして返却したいカラムを列挙します。

tableapp/serializers.py

# coding: utf-8

from rest_framework import serializers

from .models import Addr


class AddrSerializer(serializers.ModelSerializer):
    class Meta:
        model = Addr
        fields = ('id', 'name', 'birthday', 'gender', 'address', 'jobs', 'note', 'created_at', 'updated_at')

4.2 ViewSet追加

ViewSetとは、APIのView(通常で言うところのコントローラ)を抽象化したクラスです。自動的に、createや、destroyを定義してくれます。

以下の設定はModels.pyに依存する場合の記述方法です。モデルに依存しない場合は、viewsets.ViewSetを使用できます。
ここでは、APIを定義することを優先するので、簡略化して記述します。

tableapp/views.py

# coding: utf-8

import django_filters
from rest_framework import viewsets, filters

from .models import Addr
from .serializer import AddrSerializer


class AddrViewSet(viewsets.ModelViewSet):
    queryset = Addr.objects.all()
    serializer_class = AddrSerializer

4.3 Urlpatterm追加

URL設定を追加します。

以下はREST APIのURLを設定します。
tableapp/urls.py

# coding: utf-8

from rest_framework import routers
from .views import AddrViewSet


router = routers.DefaultRouter()
router.register(r'addrs', AddrViewSet)

以下は、全体的なプロジェクト側のURL設定です。「#追加部分」を追加します。

table_rest_api/urls.py

from django.contrib import admin
from django.urls import path

from django.urls import include                       # 追加
from tableapp.urls import router as tableapp_router  # 追加  

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include(tableapp_router.urls)),      # 追加
]

以下で起動します。

$ pipenv run python manage.py runserver 0.0.0.0:18899

4.4 CORS追加

CORSとは、Cross-Origin Resource Sharingの略です。Webブラウザから別ドメインにアクセスするのを可能にする仕組みです。

通常、HTML(を含むJavaScript)内からダウンロードサイトからしかアクセスできないよう、
Webブラウザが実装していますが、CORSの仕組みを使うと別ドメインへアクセスできます。

つまり、CORSの設定は、JavaScriptが別ドメインにアクセスしなければ基本的に不要です。

設定方法は以下の通りです。

table_rest_api/settings.py

INSTALLED_APPS = [
    ...(略)
    'corsheaders',
]

MIDDLEWARE = [
    ...(略)
    'corsheaders.middleware.CorsMiddleware',
]

CORS_ORIGIN_ALLOW_ALL=True

4.5 APIアクセス

以下でAPIコンソールにアクセスできます。
http://192.168.132.129:18899/api

http://192.168.132.129:18899/api/addrs
にブラウザからアクセスすると、以下のような画面を表示します。

curlすると通常のJSONが取得できます。

$ curl http://192.168.132.129:18899/api/addrs
[{"id":1,"name":"tanino","birthday":"2019-08-16","gender":0,"address":"ここではないどこか","jobs":"なし","note":"てすと","created_at":"2019-08-16T12:43:52.331031+09:00","updated_at":"2019-08-16T12:43:52.331050+09:00"}]

4.6 ディレクトリ構成

ディレクトリ構造は以下の通りです。

table_rest_api/
|--db.sqlite3
|--manage.py
|--table_rest_api
|  |--__init__.py
|  |--settings.py
|  |--urls.py
|  |--wsgi.py
|--tableapp
|  |--__init__.py
|  |--admin.py
|  |--apps.py
|  |--migrations
|  |  |--0001_initial.py
|  |  |--__init__.py
|  |--models.py
|  |--serializer.py
|  |--tests.py
|  |--urls.py
|  |--views.py

5. OpenAPIでAPI仕様書の自動生成

ちょっと前は、SwaggerといわれていたAPI定義方法です。見栄えもよいので、設定しておこうと思います。

5.1 スキーマ

スキーマは以下のように生成します。といっても、実際にはスキーマを出力しても使うところがありません。

$ pipenv install uritemplate pyyaml
$ pipenv run python manage.py generateschema --format openapi > schema.yml

以下のようなschema.ymlが自動生成されます。

openapi: 3.0.2
info:
  title: ''
  version: TODO
paths:
  /api/addrs:
    get:
      operationId: ListAddrs
      parameters: []
      responses:
        '200':
          content:
            application/json:
              schema:
                required:
                - name
                - birthday
                - gender
                - address
                - jobs
....

5.2 API定義表示(swagger-ui)

swaggerのAPI定義が表示できるように設定しておきます。

プロジェクト側(table_rest_api側)のurls.pyに以下を追加しておきます。
API側のURL設定にしなくても動作するようです。

table_rest_api/urls.py

from django.views.generic import TemplateView

from rest_framework.schemas import get_schema_view
from rest_framework.renderers import JSONOpenAPIRenderer

schema_view = get_schema_view(title='Addresses API',
                              url='https://192.168.132.129/api/',
                              renderer_classes=[JSONOpenAPIRenderer]
                              )

path('openapi/', get_schema_view(title="Addresses",
                                description="Addresses API",
                                ), name='openapi-schema'),
path('swagger-ui/', TemplateView.as_view(template_name='swagger-ui.html',
                                        extra_context={'schema_url':'openapi-schema'}
                                        ), name='swagger-ui'),

あとはtemplates/swagger-ui.htmlを設定しました。以下は参考文献の通りにしただけです。

<!DOCTYPE html>
<html>
  <head>
    <title>Swagger</title>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" href="//unpkg.com/swagger-ui-dist@3/swagger-ui.css" />
  </head>
  <body>
    <div id="swagger-ui"></div>
    <script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
    <script>
    const ui = SwaggerUIBundle({
        url: "{% url schema_url %}",
        dom_id: '#swagger-ui',
        presets: [
          SwaggerUIBundle.presets.apis,
          SwaggerUIBundle.SwaggerUIStandalonePreset
        ],
        layout: "BaseLayout"
      })
    </script>
  </body>
</html>

以下にアクセスすると、API定義が出てきます。さほど設定していないのに出力出来て便利です。
http://192.168.132.129:18899/swagger-ui/

6.おわりに

ここではAPIの設定だけ行いました。

次はAPIを使用したnuxt.jsのサンプルを作成したいと思います。

7.参考

  • Django REST Frameworkの参考
    https://qiita.com/kimihiro_n/items/86e0a9e619720e57ecd8
    https://qiita.com/Morinikiz/items/c2af4ffa180856d1bf30
  • OpenAPI仕様
    https://www.django-rest-framework.org/topics/documenting-your-api/
  • OpenAPI サンプル
    https://github.com/matthewhegarty/rest-framework-tutorial/tree/swagger-ui

-スクリプトのお勉強, 技術

執筆者:

関連記事

SimpleHTTPSAuthUploadServer というPython用モジュールを書いた

書いた動機 リモート開発になっているので、遠くのホストにあるファイルをお手軽に見たい、取ってきたいor編集したい、というニーズが、私の中であります。ftpなんか使わず、全部ブラウザでやりたいわけです。 …

IPS/IDS(Suricata)のインストール

とても遅まきながら、暇なのでIPS/IDSをこのサイトに組み込んでみます。 Suricata SuricataというOSSのIPSがあるそうなのでインストールしてみます。 環境 CentOS7(7.9 …

最近の故障/修正物

近年で故障し、しょうがないから修理、というか購入したもの、及び顛末を書いてみます。 ハードディスク・レコーダー ちょっと前から調子が悪く、電源がつかなかったり、ついたと思ったら、録画物のリストがおかし …

Webフレームワーク「Django 3.2」を使ってみる

LTSリリースなので、現状でアップデートしてないDjangoプロジェクトをDjango 3.2にしていこうかと思いました。例として、自分のDjango 2.0のプロジェクトをDjango 3.2にしま …

Python(prophet)で体重予測

最近ダイエットしていて、少しだけ成果が出たので、グラフ表示しようと思ってました。そのついでにこれからの予測もしてみようということで、過去のデータを集め、グラフ表示してみます。 google Colab …

google オプトアウト Click here to opt-out.