LoginSignup
2
3

DjangoRestFrameworkを用いたWebAPI構築基礎(heroku DBサーバ使用とデプロイ)

Last updated at Posted at 2024-04-11

概要

この記事は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によって以下の画面が表示することができることを前提とする.
ad62a72f-9820-710b-48d8-3732916e5d02.png

初期状態での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を選択する.

スクリーンショット 2024-04-11 11.19.41.png

App nameに任意のプロジェクト名を記述し,Create appを選択する.

あとでdjangoプロジェクトに記述するので覚えておくこと.

スクリーンショット 2024-04-11 11.21.34.png

ここからはDBサーバの設定を行う

Resourcesを押して,Add-onsの検索バーにheroku postgresと検索し,表示されたHeroku Postgresを選択する
スクリーンショット 2024-04-11 11.25.24.png

プロジェクトにあったDBのプランを選択する.

適切な Heroku Postgres プランの選択
https://devcenter.heroku.com/ja/articles/heroku-postgres-plans

スクリーンショット 2024-04-11 11.28.39.png

プランを選択したら,Submit Order Formを選択する.

今回はEssential2(Beta)-Freeを選択する.

スクリーンショット 2024-04-11 11.28.46.png

数分後になるとDBサーバの接続情報が見られるので確認する.
確認方法はHeroku Postgresを押してHeroku Dataにアクセスする.

スクリーンショット 2024-04-11 11.36.48.png

Settingsを選択し,View Credients...を選択するとDBサーバの接続情報が見られる.これを後でyourprojectname/setting.pyに入力する.

スクリーンショット 2024-04-11 11.38.52.png

Host,Database,User,Port,Password,URIが表示されるはずだ.

URIはDB管理ソフトでこのDBにアクセスするために必要である.

djangoディレクトリのセッティング

DRFの構築とDBサーバの設定,herokuにデプロイするための設定をする.

setting.pyの設定

ALLOWEDHOSTにherokuにデプロイするためにherokuのホスト名である,herokuapp.comを追記する.デプロイするのに必要である.

yourprojectname/setting.py
ALLOWED_HOSTS = [...,
                '.herokuapp.com', #この行を追加
                ]

DATABASEに先ほどheroku dashboardで作成したHeroku PostgresのDBサーバの接続情報を入力する.DBサーバを利用するのに必要である.

yourprojectname/setting.py
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の使用に必要である.

yourprojectname/setting.py
INSTALLED_APPS = [
    ...,
    'yourappname',    # アプリ名も記述
    'rest_framework', # この行を追加
]

MIDDLEWAREに以下の情報を追記し,STATIC_ROOT設定を追加する.デプロイ時に静的ファイルを扱うのに必要である.

yourprojectname/setting.py
MIDDLEWARE = [
    ...,
    'whitenoise.middleware.WhiteNoiseMiddleware',
]

import os
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

新しくREST_FRAMEWORK 設定を追加する.REST_FRAMEWORK 設定は、APIのデフォルトのパーミッションクラスを指定する.DRFの使用に必要である.

yourprojectname/setting.py
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
    ]
}

モデル,ビュー,シリアライザーの作成

デプロイするのみの人は不要だがAPIをデプロイするなら必要である.

モデルの作成
API通信をするデータの構造を定義するmodels.pyを作成する.

yourappname/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属性を入れる必要がある.

yourappname/serializers.py

from rest_framework import serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ['id', 'title', 'content']
        

ビューの作成
ビューはリクエストを処理し,レスポンスを返す.DRFでは,ビューセットを使用してCRUD(作成,読み取り,更新.削除)操作を簡単に実装できる.

yourappname/view.py
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パスを定義する.このエンドポイントにアクセスしてデータのやり取りを行う.

yourprojectname/urls.py
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バージョンを指定するファイル.

runtime.txt
python-3.12.2 # プロジェクトで使用しているPythonのバージョンにすること

requirements.txt
pipからインストールしたものを記載する.デプロイ時のwebサーバでインストールする際に必要である.

requirements.txt
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を記述することをお勧めする.プッシュ,プル,マージの際にコンフリクトが起こる可能性があるからである.

.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の検索バーに先ほどのリポジトリを入力する.
スクリーンショット 2024-04-11 13.50.48.png
Connectを選択する.
スクリーンショット 2024-04-11 13.53.26.png
Manual deployでデプロイするブランチを選択してDeploy Branchを押せばデプロイ完了.
スクリーンショット 2024-04-11 13.54.21.png

Automatic deploysでデプロイするブランチを選択して,Enable Automatic Deploysを選択すると,gitにプッシュするときに自動でデプロイしてくれるようになる.
スクリーンショット 2024-04-11 14.04.47.png

ページ上部のOpen Appを押せば以下の画面が表示される.
スクリーンショット 2024-04-11 14.11.13.png

これでAPIが作成できた.

APIクライアント受け取り設定

Androidクライアント側でのAPI受け取り設定

androidappname/app/src/main/java/com/example/androidappname/Post.java

package com.example.androidappname;

public class Post {

    private int id;

    private String name;

    private String content

}
androidappname/app/src/main/java/com/example/androidappname/ApiClient.java
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受け取り設定

Post.cs
[System.Serializable]
public class Post
{
    public int id;
    public string name;
    public string content;
}

ApiClient.cs
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での受け取り方

Post.swift
import Foundation
struct Post: Codable, Identifiable {
    let id: Int
    let name: String
    let content: String

}
APIClient.swift
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()
    }
}

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3