概要
この記事はDjangoRestFramework(以下DRFと記す)を用いたWebAPIを作成する方法を説明する.DRFはPythonのWebフレームワークであるDjangoの拡張機能で,RESTful Web APIの開発を容易にするために設計されている.
API(Application Programming Interface)
異なるソフトウェアコンポーネント間でデータを交換し,機能を相互利用するための規約やルール,ツールの集合
Restful Web API
Webアプリケーションやサービス間で情報をやり取りするためのインターフェースであり,REST原則に基づいて設計されている.REST(Representational State Transfer)
インターネット上での分散システム設計(クライアントサーバシステム)のためのアーキテクチャスタイルを定義し,シンプルさと拡張性を提供します。RESTful APIはHTTPプロトコルを使用してデータやサービスにアクセスし,操作する.
主な操作メソッド
RESTful APIでは,主に以下のHTTPメソッドを使用してリソースに対する操作を行う.
GET リソースを取得する.
POST 新しいリソースを作成する.
PUT リソースを更新する.
DELETE リソースを削除する.
PATCH リソースの一部を更新する.
これらをAPIのエンドポイントに対して使用し,データ通信の命令をする.
難しいかもしれないが前提知識として知っておいてほしい.
今回はPaaSでherokuにデプロイし,DBサーバにheroku postgresを用いる.
前提条件
ローカルサーバーの構築までは以下のページを参照
[Django初心者]Djangoでのwebアプリの作成[基礎編〜ローカルサーバー構築まで〜]
https://qiita.com/tarakokko3233/items/46c567fb32a26a69269a
この記事では$ Python manage.py runserver
によって以下の画面が表示することができることを前提とする.
初期状態でのDjangoプロジェクトの階層構造
yourprojectname/
manage.py
yourprojectname/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
yourappname/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
Djangoディレクトリの階層説明を以下の記事で詳しく説明している.
https://qiita.com/tarakokko3233/items/8787692aaf9b1d943205
環境構築
仮想環境の構築
仮想環境が有効になっていることを確認する.
もし有効でないならば以下のコマンドを実行する.
Mac用
$ source venv/bin/activate
Windows用
$ venv\Scripts\activate
仮想環境が作成できていないのならば以下のコマンドを実行する.
$ python -m venv venv
DRFのインストール
仮想環境にDRFをインストールする.
$ pip install djangorestframework
Psycopg2のインストール
psycopg2はPostgreSQLデータベースに接続するためのPythonのアダプターである.このライブラリを使うことで,PythonプログラムからPostgreSQLデータベースに対してSQLクエリを実行したり,データを操作したりできる.
$ pip install psycopg2
Apple シリコンMacなどpsycopg2でうまくいかなかった場合以下のコードでインストールする.
$ pip install psycopg2-binary
Dj-database-urlのインストール
データベース接続情報をHerokuの環境変数から読み込むために役立つライブラリ.
$ pip install dj-database-url
gunicornのインストール
HerokuでDjangoアプリケーションを実行するためのWSGI HTTPサーバー.
$ pip install gunicorn
Whitenoiseのインストール
静的ファイルの提供を容易にするためのライブラリ.Heroku上でDjangoアプリをデプロイする際に必要.
$ pip install whitenoise
heroku dashboardの設定(DBサーバとデプロイ設定)
herokuプロジェクトの作成
以下にアクセスしてheroku dashboardを開く.
https://dashboard.heroku.com/apps
もしアカウントを作成していないならばherokuアカウントを作成すること.
開いたら,heroku dashboardのnew
を押して,Create new app
を選択する.
App nameに任意のプロジェクト名を記述し,Create app
を選択する.
あとでdjangoプロジェクトに記述するので覚えておくこと.
ここからはDBサーバの設定を行う
Resources
を押して,Add-onsの検索バーにheroku postgres
と検索し,表示されたHeroku Postgres
を選択する
プロジェクトにあったDBのプランを選択する.
適切な Heroku Postgres プランの選択
https://devcenter.heroku.com/ja/articles/heroku-postgres-plans
プランを選択したら,Submit Order Form
を選択する.
今回はEssential2(Beta)-Freeを選択する.
数分後になるとDBサーバの接続情報が見られるので確認する.
確認方法はHeroku Postgres
を押してHeroku Dataにアクセスする.
Settings
を選択し,View Credients...
を選択するとDBサーバの接続情報が見られる.これを後でyourprojectname/setting.py
に入力する.
Host,Database,User,Port,Password,URIが表示されるはずだ.
URIはDB管理ソフトでこのDBにアクセスするために必要である.
djangoディレクトリのセッティング
DRFの構築とDBサーバの設定,herokuにデプロイするための設定をする.
setting.pyの設定
ALLOWEDHOSTにherokuにデプロイするためにherokuのホスト名である,herokuapp.com
を追記する.デプロイするのに必要である.
ALLOWED_HOSTS = [...,
'.herokuapp.com', #この行を追加
]
DATABASEに先ほどheroku dashboardで作成したHeroku PostgresのDBサーバの接続情報を入力する.DBサーバを利用するのに必要である.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',#どのDBでもPostgreSQLなら同じ
'NAME': '-', # Heroku Postgresで確認したDatabaseの値を入力
'USER': '-', # Heroku Postgresで確認したUserの値を入力
'PASSWORD': '-', # Heroku Postgresで確認したPasswordの値を入力
'HOST': '-', # Heroku Postgresで確認したHostの値を入力
'PORT': '5432', # どのDBでもPostgreSQLなら同じ
}
}
INSTALLED_APPSに以下の情報を追記する.DRFの使用に必要である.
INSTALLED_APPS = [
...,
'yourappname', # アプリ名も記述
'rest_framework', # この行を追加
]
MIDDLEWAREに以下の情報を追記し,STATIC_ROOT設定を追加する.デプロイ時に静的ファイルを扱うのに必要である.
MIDDLEWARE = [
...,
'whitenoise.middleware.WhiteNoiseMiddleware',
]
import os
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
新しくREST_FRAMEWORK 設定を追加する.REST_FRAMEWORK 設定は、APIのデフォルトのパーミッションクラスを指定する.DRFの使用に必要である.
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
]
}
モデル,ビュー,シリアライザーの作成
デプロイするのみの人は不要だがAPIをデプロイするなら必要である.
モデルの作成
API通信をするデータの構造を定義するmodels.py
を作成する.
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
def __str__(self):
return self.title
ここでは簡単にタイトルとその内容を格納するPostモデルを作成する.
モデルを作成したら以下のコマンドを実行してデータベースに適用させる必要がある.
$ python manage.py makemigrations
$ python manage.py migrate
シリアライザーの作成
シリアライザーは,モデルインスタンスとデータ型(JSONなど)との間の変換を行う.serializers.py
は初期では存在しないので追加でファイル作成をしてほしい.Djangoはモデルをマイグレートすると,自動的に主キーとしてid属性が振られるのでシリアライザーにはid属性を入れる必要がある.
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content']
ビューの作成
ビューはリクエストを処理し,レスポンスを返す.DRFでは,ビューセットを使用してCRUD(作成,読み取り,更新.削除)操作を簡単に実装できる.
from rest_framework import viewsets
from .models import Post
from .serializers import PostSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
Post.objects.all()
で,モデルの全レコードを取得し,serializer_class
に作成したPostモデルのシリアライザーを取得する.
ルーティング(エンドポイント)の設定
リクエストを適切なビューにルーティングする必要があるので,urls.pyにURLパスを定義する.このエンドポイントにアクセスしてデータのやり取りを行う.
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import PostViewSet
router = DefaultRouter()
router.register(r'posts', PostViewSet)
urlpatterns = [
path('', include(router.urls)),
]
これでPostモデルに対するCRUD操作を行うエンドポイントが設定される.例えば,/posts/へのGETリクエストはデータのリストを返し,POSTリクエストは新しいデータを作成する.
デプロイ設定
herokuにデプロイするときはプロジェクトを認識させるためにファイルを作成する必要がある.
manage.pyがあるディレクトリに以下の3つのファイルを追加する.
Procfile
HerokuでDjangoアプリケーションをデプロイする際に,Herokuがアプリケーションをどのように実行すべきかを指示するファイル.
web: gunicorn yourprojectname.wsgi --log-file -
runtime.txt
デプロイ時のwebサーバで使うpythonバージョンを指定するファイル.
python-3.12.2 # プロジェクトで使用しているPythonのバージョンにすること
requirements.txt
pipからインストールしたものを記載する.デプロイ時のwebサーバでインストールする際に必要である.
Django
djangorestframework
psycopg2
dj-database-url
gunicorn
whitenoise
//他にpipからインストールしたものがあれば記載する
gitへプッシュ
ここまで作成した物をgitにプッシュする.そして,git上にあるリポジトリをデプロイすることになるため,以下のディレクトリ構造にしてプッシュすること.
manage.py
Procfile
runtime.txt
requirements.txt
.gitignore
yourprojectname/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
yourappname/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
serializers.py
tests.py
views.py
manage.pyがある階層がルートになるようにgitにプッシュすること.
また仮想環境やキャッシュファイルがgitにプッシュされないように.gitignoreを記述することをお勧めする.プッシュ,プル,マージの際にコンフリクトが起こる可能性があるからである.
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
media/
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.venv
venv/
ENV/
env.bak/
venv.bak/
/venv/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# VS Code settings
.vscode/
# JupyterLab
.jupyterlab/
# DS_Store
.DS_Store
herokuにデプロイ
heroku dashboardにアクセスしてyourprojectnameを選択し,deployを選択する.
Deplayment methodにGitHubを選択し,Connect to GitHubの検索バーに先ほどのリポジトリを入力する.
Connectを選択する.
Manual deployでデプロイするブランチを選択してDeploy Branchを押せばデプロイ完了.
Automatic deploysでデプロイするブランチを選択して,Enable Automatic Deploysを選択すると,gitにプッシュするときに自動でデプロイしてくれるようになる.
ページ上部のOpen Appを押せば以下の画面が表示される.
これでAPIが作成できた.
APIクライアント受け取り設定
Androidクライアント側でのAPI受け取り設定
package com.example.androidappname;
public class Post {
private int id;
private String name;
private String content
}
package com.example.androidappname;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ApiClient {
private static final String BASE_URL = "デプロイしたherokuプロジェクトのURL";
private static Retrofit retrofit = null;
public static ApiService getApiService() {
if (retrofit == null) {
// ロギングインターセプタを作成
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
// カスタムOkHttpClientを作成
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(logging); // ロギングインターセプタを追加
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build()) // カスタムOkHttpClientを使用
.build();
}
return retrofit.create(ApiService.class);
}
}
UnityでのAPI受け取り設定
[System.Serializable]
public class Post
{
public int id;
public string name;
public string content;
}
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class APIHandler : MonoBehaviour
{
private readonly string apiUrl = "デプロイしたherokuプロジェクトのURL"; // APIのエンドポイントを指定
// APIからデータを取得するメソッド
public IEnumerator GetUserData()
{
UnityWebRequest request = UnityWebRequest.Get(apiUrl);
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
// レスポンスのJSONをUserModelオブジェクトにデシリアライズ
Post postdata = JsonUtility.FromJson<Post>(request.downloadHandler.text);
Debug.Log("User Name: " + postdata.name); // デバッグログにユーザー名を表示
}
else
{
Debug.LogError("Error: " + request.error);
}
}
// スタート時にAPIからデータを取得
void Start()
{
StartCoroutine(GetUserData());
}
}
Swiftでの受け取り方
import Foundation
struct Post: Codable, Identifiable {
let id: Int
let name: String
let content: String
}
import Foundation
class APIClient {
static let shared = APIClient()
private let baseURL = "デプロイしたherokuプロジェクトのURL"
func getRequest<T: Decodable>(path: String, completion: @escaping (Result<T, Error>) -> Void) {
guard let url = URL(string: baseURL + path) else {
completion(.failure(NSError(domain: "Invalid URL", code: 0, userInfo: nil)))
return
}
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.failure(NSError(domain: "No data", code: 0, userInfo: nil)))
return
}
do {
let decoder = JSONDecoder()
let result = try decoder.decode(T.self, from: data)
completion(.success(result))
} catch {
completion(.failure(error))
}
}
task.resume()
}
}