LoginSignup
15
11

Django + REST framework + Django Debug Toolbar + JWT + Swagger + docker-compose の環境構築

Last updated at Posted at 2019-04-27

目的

Mac上に開発環境、Dokcerに本番環境を構築し、下記URLにブラウザアクセスが可能な状態にする。

Page URL
Django (Hello, world.) http://localhost:8000
Django administration http://localhost:8000/admin/
REST framewrk http://localhost:8000/api/
Django Debug Toolbar -
Swagger http://localhost:8000/api/schema/swagger-ui
Django REST framework JWT http://localhost:8000/api-auth/

環境

MacOSX: Sonoma
Python: 3.11.1
Django: 4.2.11
Django REST framework: 3.14.0
Django Debug Toolbar: 4.3.0
drf-spectacular(swagger): 0.27.1
Simple JWT: 5.3.1

Django 開発環境構築

作業ディレクトリ用意

% mkdir Django4.2-DRF && cd Django4.2-DRF

Pythonのセットアップ

% pyenv versions        # インストールされているバージョンを確認
% pyenv install --list  # インストール可能なバージョンを表示
% pyenv install 3.11.1  # Python3.11.1をインストール
% pyenv local 3.11.1    # このディレクトリ配下のバージョンを固定

ローカルリポジトリの作成

% git init
% echo '# Django4.2-DRF' > README.md
% cat << EOF > .gitignore
*.pyc
__pycache__
db.sqlite3
venv/*
EOF
% git add .
% git commit -m 'first commit'

↓ この作業後のディレクトリ構成

Django4.2-DRF
├── .git
├── .gitignore
└── README.md

Django用作業ディレクトリの作成

% mkdir django && cd django
% poetry init
This command will guide you through creating your pyproject.toml config.

Package name [django4.2-drf]:
Version [0.1.0]:
Description []:
Author [mykysyk, n to skip]:
License []:
Compatible Python versions [^3.11]:

Would you like to define your main dependencies interactively? (yes/no) [yes]
You can specify a package in the following forms:
  - A single name (requests): this will search for matches on PyPI
  - A name and a constraint (requests@^2.23.0)
  - A git url (git+https://github.com/python-poetry/poetry.git)
  - A git url with a revision (git+https://github.com/python-poetry/poetry.git#develop)
  - A file path (../my-package/my-package.whl)
  - A directory (../my-package/)
  - A url (https://example.com/packages/my-package-0.1.0.tar.gz)

Package to add or search for (leave blank to skip):

Would you like to define your development dependencies interactively? (yes/no) [yes]
Package to add or search for (leave blank to skip):

Generated file

[tool.poetry]
name = "django4-2-drf"
version = "0.1.0"
description = ""
authors = ["mykysyk"]
readme = "README.md"
packages = [{include = "django"}]

[tool.poetry.dependencies]
python = "^3.11"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"


Do you confirm generation? (yes/no) [yes]

↓ この作業後のディレクトリ構成

Django4.2-DRF
├── .git
├── .gitignore
├── .python-version
├── README.md
└── django
    └── pyproject.toml

Djangoのインストール

Django4.2-DRF
└── django ← 現在の階層

https://www.djangoproject.com/download/ より最新LTSのバージョンを調べてインストール

% poetry add Django==4.2.11
% poetry run python -m django --version
4.2.11

Djangoプロジェクト作成

% poetry run django-admin startproject config .

↓ この作業後のディレクトリ構成

Django4.2-DRF
├── .git
├── .gitignore
├── .python-version
├── README.md
└── django
    ├── .venv
    ├── config               # プロジェクトが作成された
    │   ├── __init__.py     # プロジェクトが作成された
    │   ├── asgi.py         # プロジェクトが作成された
    │   ├── settings.py     # プロジェクトが作成された
    │   ├── urls.py         # プロジェクトが作成された
    │   └── wsgi.py         # プロジェクトが作成された
    ├── manage.py            # プロジェクトが作成された
    ├── poetry.lock
    └── pyproject.toml

管理画面にログインするスーパーユーザーを追加

データベース構築

% poetry run python manage.py migrate

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
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 auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK

↓ この作業後のディレクトリ構成

├── .git
├── .gitignore
├── .python-version
├── README.md
└── django
    ├── config
    │   ├── __init__.py
    │   ├── __pycache__
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── db.sqlite3  # DBが新規作成される
    ├── manage.py
    ├── poetry.lock
    └── pyproject.toml

スーパーユーザ作成

% poetry run python manage.py createsuperuser

動作確認で起動してみる

% poetry run python manage.py runserver
確認 1: http://localhost:8000 にアクセスし「The install worked successfully! Congratulations!」が出ればOK
確認 2: http://localhost:8000/admin/sample_app/memo/ にアクセスできるのを確認
login画面 ログイン後

ここまでの変更をGitでコミット

git add .
git commit -m 'Djangoプロジェクト作成'

sample_app アプリケーション作成

% poetry run python manage.py startapp sample_app

↓ この作業後のディレクトリ構成

Django4.2-DRF
├── .git
├── .gitignore
├── .python-version
├── README.md
└── django
    ├── .venv
    ├── config
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── manage.py
    ├── poetry.lock
    ├── pyproject.toml
    └── sample_app          # アプリケーションが作成された
        ├── __init__.py     # アプリケーションが作成された
        ├── admin.py        # アプリケーションが作成された
        ├── apps.py         # アプリケーションが作成された
        ├── migrations      # アプリケーションが作成された
        ├── models.py       # アプリケーションが作成された
        ├── tests.py        # アプリケーションが作成された
        └── views.py        # アプリケーションが作成された

sample_app のアプリケーション登録

編集するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── config
        └── settings.py ← これ
% vi config/settings.py

既存ファイルとの差分

django/config/settings.py
% git diff config/settings.py

diff --git a/django/config/settings.py b/django/config/settings.py
index be04b32..33ec9e6 100644
--- a/django/config/settings.py
+++ b/django/config/settings.py
@@ -31,6 +31,7 @@ ALLOWED_HOSTS = []
 # Application definition

 INSTALLED_APPS = [
+    'sample_app',
     'django.contrib.admin',
     'django.contrib.auth',
     'django.contrib.contenttypes',

ビュー作成

編集するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── sample_app
        └── views.py ← これ
% vi sample_app/views.py
django/sample_app/views.py
from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, world.")

アプリ用URLConfの新規作成

編集するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── sample_app
        └── urls.py ← これ新規作成
% vi sample_app/urls.py
django/sample_app/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

ルート用URLConfの編集

編集するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── config
        └── urls.py ← これ
% vi config/urls.py

既存ファイルとの差分

django/config/urls.py
% git diff config/urls.py
diff --git a/django/config/urls.py b/django/config/urls.py
index 5006338..d212c09 100644
--- a/django/config/urls.py
+++ b/django/config/urls.py
@@ -15,8 +15,9 @@ Including another URLconf
     2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
 """
 from django.contrib import admin
-from django.urls import path
+from django.urls import path, include

 urlpatterns = [
+    path('', include('sample_app.urls')),
     path('admin/', admin.site.urls),
 ]

動作確認で起動してみる

% poetry run python manage.py runserver
確認: http://localhost:8000 にアクセスし「Hello, world.」が出ればOK

ここまでの変更をGitでコミット

% git add .
% git commit -m 'Djangoアプリケーション作成'

Django REST framework

Django REST Framework で RESTful API バックエンドを構築

Django REST framework のインストール

Django4.2-DRF
└── django ← 現在の階層
% poetry add djangorestframework django-filter

Djagno REST Framework のアプリケーション登録

編集するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── config
        └── settings.py ← これ
% vi config/settings.py

既存ファイルとの差分

django/config/settings.py
% git diff django/config/settings.py

diff --git a/django/config/settings.py b/django/config/settings.py
index 75b5a5e..d23b69d 100644
--- a/django/config/settings.py
+++ b/django/config/settings.py
@@ -31,6 +31,7 @@ ALLOWED_HOSTS = []
 # Application definition

 INSTALLED_APPS = [
+    'rest_framework',
     'sample_app',
     'django.contrib.admin',
     'django.contrib.auth',

Modelsの定義

データが必要とするフィールドとその動作を定義

編集するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── sample_app
        └── models.py ← これ
% vi sample_app/models.py
django/sample_app/models.py
from django.db import models


class Memo(models.Model):
    title = models.CharField(max_length=64)
    memo = models.TextField(max_length=1024)

Serializerの定義

「入力データ(jsonデータ)とモデルオブジェクトの相互変換」
「入力データのバリデーション」

新規作成するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── sample_app
        └── serializer.py ← 新規作成
% vi sample_app/serializer.py
sample_app/serializer.py
from rest_framework import serializers
from .models import Memo


class MemoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Memo
        fields = '__all__'

↓ この作業によりできるディレクトリ構成

Django4.2-DRF
├── .git
├── .gitignore
├── .python-version
├── .venv
├── README.md
└── django
    ├── .venv
    ├── config
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── db.sqlite3
    ├── manage.py
    ├── poetry.lock
    ├── pyproject.toml
    └── sample_app
        ├── __init__.py
        ├── __pycache__
        ├── admin.py
        ├── apps.py
        ├── migrations
        ├── models.py
        ├── serializer.py  # 新規作成
        ├── tests.py
        ├── urls.py
        └── views.py

ViewSetの追加

編集するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── sample_app
        └──  views.py ← これ
% vi sample_app/views.py
django/sample_app/views.py
from django.http import HttpResponse
from rest_framework import viewsets
from .models import Memo
from .serializer import MemoSerializer


def index(request):
    return HttpResponse("Hello, world.")


class MemoViewSet(viewsets.ModelViewSet):
    queryset = Memo.objects.all()
    serializer_class = MemoSerializer

アプリ用URLConfの編集

REST frameworkへの設定を組み込む

編集するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── sample_app
        └── urls.py ← これ
% vi sample_app/urls.py

既存ファイルとの差分

django/sample_app/urls.py
% git diff sample_app/urls.py

diff --git a/django/sample_app/urls.py b/django/sample_app/urls.py
index 88a9cac..43c2a4d 100644
--- a/django/sample_app/urls.py
+++ b/django/sample_app/urls.py
@@ -1,7 +1,11 @@
 from django.urls import path

 from . import views
+from rest_framework import routers

 urlpatterns = [
     path('', views.index, name='index'),
 ]
+
+router = routers.DefaultRouter()
+router.register(r'memo', views.MemoViewSet)

ルート用URLConfの編集

編集するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── config
        └── urls.py ← これ
% vi config/urls.py

既存ファイルとの差分

django/config/urls.py
 % git diff config/urls.py
 
diff --git a/django/config/urls.py b/django/config/urls.py
index d212c09..6503349 100644
--- a/django/config/urls.py
+++ b/django/config/urls.py
@@ -16,8 +16,10 @@ Including another URLconf
 """
 from django.contrib import admin
 from django.urls import path, include
+from sample_app.urls import router as sample_app_router

 urlpatterns = [
     path('', include('sample_app.urls')),
     path('admin/', admin.site.urls),
+    path('api/', include(sample_app_router.urls)),
 ]

データベース再構築

マイグレーションファイル作成

% poetry run python manage.py makemigrations

Migrations for 'sample_app':
  sample_app/migrations/0001_initial.py
    - Create model Memo

再構築

% poetry run python manage.py migrate

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sample_app, sessions
Running migrations:
  Applying sample_app.0001_initial... OK

↓ この作業後のディレクトリ構成

Django4.2-DRF
├── .git
├── .gitignore
├── .python-version
├── .venv
├── README.md
├── django
│   ├── config
│   │   ├── __init__.py
│   │   ├── asgi.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   ├── db.sqlite3
│   ├── manage.py
│   └── sample_app
│       ├── __init__.py
│       ├── admin.py
│       ├── apps.py
│       ├── migrations
│       │   ├── 0001_initial.py  # makemigrationsによって作成
│       │   └── __init__.py
│       ├── models.py
│       ├── tests.py
│       ├── urls.py
│       └── views.py
├── poetry.lock
└── pyproject.toml

管理画面にsample_appのモデルを追加する

編集するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── sample_app
        └── admin.py ← これ
% vi sample_app/admin.py
django/sample_app/admin.py
from django.contrib import admin
from .models import Memo


@admin.register(Memo)
class MemoAdmin(admin.ModelAdmin):
    pass

動作確認で起動してみる

% poetry run python manage.py runserver
確認 1: http://localhost:8000/api/memo/ にアクセスできるか確認
Django REST Framework
確認 2: http://localhost:8000/admin/sample_app/memo/ にアクセスできるか確認
ログイン後

ここまでの変更をGitでコミット

git add .
git commit -m 'DRF組み込み'

Django Debug Toolbar

Django Debug Toolbarでデバッグ情報を表示

Django Debug Toolbar のインストール

Django4.2-DRF
└── django ← 現在の階層
% poetry add django-debug-toolbar

Django Debug Toolbar の有効化

編集するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── config
        └── settings.py ← これ
% vi config/settings.py
djangoconfig/settings.py
% git diff config/settings.py

diff --git a/django/config/settings.py b/django/config/settings.py
index aa06725..1b42fa6 100644
--- a/django/config/settings.py
+++ b/django/config/settings.py
@@ -123,3 +123,18 @@ STATIC_URL = 'static/'
 # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field

 DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
+
+if DEBUG:
+    STATIC_ROOT = None
+    STATICFILES_DIRS = ['static']
+    # django-debug-toolbar
+    # https://django-debug-toolbar.readthedocs.io/en/latest/index.html
+    INSTALLED_APPS += ['debug_toolbar']
+    MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']
+    DEBUG_TOOLBAR_CONFIG = {'SHOW_TOOLBAR_CALLBACK': lambda request: True}
+else:
+    import os
+    STATIC_URL = 'static/'
+    STATIC_ROOT = os.path.join(BASE_DIR, 'static')
+    STATICFILES_DIRS = []
+    ALLOWED_HOSTS = ['*']

ルート用URLConfの編集

Django4.2-DRF
└── django ← 現在の階層
    └── config
        └── urls.py ← これ
% vi config/urls.py
django/config/urls.py
% git diff config/urls.py

diff --git a/django/config/urls.py b/django/config/urls.py
index d956986..9242764 100644
--- a/django/config/urls.py
+++ b/django/config/urls.py
@@ -18,6 +18,7 @@ from django.contrib import admin
 from django.urls import path, include
 from sample_app.urls import router as sample_app_router
 from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
+from config import settings

 urlpatterns = [
     path('', include('sample_app.urls')),
@@ -27,3 +28,7 @@ urlpatterns = [
     path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
     path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
 ]
+
+if settings.DEBUG:
+    import debug_toolbar
+    urlpatterns += [path('__debug__/', include(debug_toolbar.urls))]

動作確認で起動してみる

% poetry run python manage.py runserver
確認: http://localhost:8000/api/memo にアクセスして確認

ここまでの変更をGitでコミット

% git add .
% git commit -m 'Django Debug Toolbar組み込み'

JWT

JWT(JSON Web Token)を利用したWebアプリケーションの認証の導入

djangorestframework-simplejwt のインストール

Django4.2-DRF
└── django ← 現在の階層
% poetry add djangorestframework-simplejwt

djangorestframework-jwt の有効化

編集するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── config
        └── settings.py ← これ
% vi config/settings.py
config/settings.py
diff --git a/django/config/settings.py b/django/config/settings.py
index 1b42fa6..e74d123 100644
--- a/django/config/settings.py
+++ b/django/config/settings.py
@@ -31,6 +31,7 @@ ALLOWED_HOSTS = []
 # Application definition

 INSTALLED_APPS = [
+    'rest_framework_simplejwt',
     'rest_framework',
     'sample_app',
     'django.contrib.admin',
@@ -124,6 +125,15 @@ STATIC_URL = 'static/'

 DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

+REST_FRAMEWORK = {
+    'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
+    'DEFAULT_AUTHENTICATION_CLASSES': (
+        'rest_framework_simplejwt.authentication.JWTAuthentication',
+        'rest_framework.authentication.SessionAuthentication',
+        'rest_framework.authentication.BasicAuthentication',
+    )
+}
+
 if DEBUG:
     STATIC_ROOT = None
     STATICFILES_DIRS = ['static']

ルート用URLConfの編集

Django4.2-DRF
└── django ← 現在の階層
    └── config
        └── urls.py ← これ
% vi config/urls.py

既存ファイルとの差分

django/config/urls.py
% git diff config/urls.py

diff --git a/django/config/urls.py b/django/config/urls.py
index c759bb5..f7ff5e8 100644
--- a/django/config/urls.py
+++ b/django/config/urls.py
@@ -18,11 +18,17 @@ from django.contrib import admin
 from django.urls import path, include
 from sample_app.urls import router as sample_app_router
 from config import settings
+from rest_framework_simplejwt.views import (
+    TokenObtainPairView,
+    TokenRefreshView,
+)

 urlpatterns = [
     path('', include('sample_app.urls')),
     path('admin/', admin.site.urls),
     path('api/', include(sample_app_router.urls)),
+    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
+    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
 ]

 if settings.DEBUG:

動作確認で起動してみる

% poetry run python manage.py runserver

トークンなしでGETリクエスト

 % curl -s -XGET http://localhost:8000/api/memo/ | jq .
結果
{
  "detail": "Authentication credentials were not provided."
}

アクセストークンとリフレッシュトークンを取得する

% DJANGO_USERNAME='admin'
% DJANGO_PASSWORD='userpassword'
% curl \
  -s \
  -X POST \
  -H "Content-Type: application/json" \
  -d "{\"username\": \"${DJANGO_USERNAME}\", \"password\": \"${DJANGO_PASSWORD}\"}" \
  http://localhost:8000/api/token/ | jq .
結果
{
  "refresh": "REFRESH_TOKEN",
  "access": "ACCESS_TOKEN"
}

アクセストークンを利用して情報を取得

トークン発行
% DJANGO_USERNAME='admin'
% DJANGO_PASSWORD='userpassword'
% ACCESS_TOKEN=$(curl \
  -s \
  -X POST \
  -H "Content-Type: application/json" \
  -d "{\"username\": \"${DJANGO_USERNAME}\", \"password\": \"${DJANGO_PASSWORD}\"}" \
  http://localhost:8000/api/token/ | jq -r .access)
情報を取得
% curl \
  -s \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  http://localhost:8000/api/memo/ | jq .
結果
[]

POSTしてデータ登録

% curl -XPOST -H "Authorization: Bearer ${ACCESS_TOKEN}" http://127.0.0.1:8000/api/memo/ -d 'title=a&memo=a'
結果
{"id":1,"title":"a","memo":"a"}

POSTデータの確認

% curl \
  -s \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  http://localhost:8000/api/memo/ | jq .
結果
[
  {
    "id": 1,
    "title": "a",
    "memo": "a"
  }
]

アクセストークンの期限が切れたらリフレッシュトークンを利用して新しいアクセストークンを再取得する。

curl \
  -s \
  -X POST \
  -H "Content-Type: application/json" \
  -d "{\"refresh\":\"${REFRESH_TOKEN}\"}" \
  http://localhost:8000/api/token/refresh/ | jq .
結果
{
  "access": "ACCESS_TOKEN"
}

ここまでの変更をGitでコミット

% git add .
% git commit -m 'JWT組み込み'

Swagger

Django Rest Framework のコードから Swagger ドキュメントを生成

drf-spectacular のインストール

Django4.2-DRF
└── django ← 現在の階層
% poetry add drf-spectacular

Swagger の有効化

編集するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── config
        └── settings.py ← これ
% vi config/settings.py
django/config/settings.py
% git diff config/settings.py

diff --git a/django/config/settings.py b/django/config/settings.py
index e74d123..e52e56f 100644
--- a/django/config/settings.py
+++ b/django/config/settings.py
@@ -31,6 +31,7 @@ ALLOWED_HOSTS = []
 # Application definition

 INSTALLED_APPS = [
+    'drf_spectacular',
     'rest_framework_simplejwt',
     'rest_framework',
     'sample_app',
@@ -131,7 +132,14 @@ REST_FRAMEWORK = {
         'rest_framework_simplejwt.authentication.JWTAuthentication',
         'rest_framework.authentication.SessionAuthentication',
         'rest_framework.authentication.BasicAuthentication',
-    )
+    ),
+    'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
+}
+
+SPECTACULAR_SETTINGS = {
+    'TITLE': 'Swagger Sample App',
+    'DESCRIPTION': '',
+    'VERSION': 'v2024.3.23',
 }

 if DEBUG:

ルート用URLConfの編集

Django4.2-DRF
└── django ← 現在の階層
    └── config
        └── urls.py ← これ
% vi config/urls.py

既存ファイルとの差分

django/config/urls.py
% git diff config/urls.py

diff --git a/django/config/urls.py b/django/config/urls.py
index f7ff5e8..df6f3c2 100644
--- a/django/config/urls.py
+++ b/django/config/urls.py
@@ -22,6 +22,7 @@ from rest_framework_simplejwt.views import (
     TokenObtainPairView,
     TokenRefreshView,
 )
+from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView

 urlpatterns = [
     path('', include('sample_app.urls')),
@@ -29,6 +30,9 @@ urlpatterns = [
     path('api/', include(sample_app_router.urls)),
     path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
     path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
+    path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
+    path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
+    path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
 ]

 if settings.DEBUG:

動作確認で起動してみる

% poetry run python manage.py runserver
確認: http://localhost:8000/api/schema/swagger-ui/ にアクセスしてswaggerが表示されているか確認
Django REST Swagger

ここまでの変更をGitでコミット

% git add .
% git commit -m 'swagger組み込み'

本番環境の構築

Django4.2-DRF
└── django ← 現在の階層

uvicorn/gunicornインストール

% poetry add uvicorn gunicorn

動作確認で起動してみる

% # --chdir /path/to/Django4.2-DRF/django の部分は
% # Djangoのconfigディレクトリがあるディレクトリを指定
% poetry run python -m gunicorn config.asgi:application \
        --worker-class uvicorn.workers.UvicornWorker \
        --workers 4 \
        --bind 0.0.0.0:8000 \
        --max-requests 500 \
        --max-requests-jitter 200 \
        --timeout 300 \
        --graceful-timeout 300 \
        --reload \
        --chdir /path/to/Django4.2-DRF/django

Django用Dockerfileの作成

新規に作成するファイル

Django4.2-DRF
└── django ← 現在の階層
    └── Dockerfile  ← 新規作成
% vi Dockerfile
Dockerfile
FROM python:3.11-slim-bookworm
ENV TZ=Asia/Tokyo \
    PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    POETRY_VIRTUALENVS_CREATE=false \
    PATH="/root/.local/bin:$PATH"

# Install Packages
RUN apt-get update && apt-get install -y curl

WORKDIR /app

# Source Copy
COPY . /app/

# Poetry Install
RUN curl -sSL https://install.python-poetry.org | python3 -
RUN poetry install --no-root

# Create DB & Static file
RUN sed -i.org 's/^DEBUG\s+=\s+True$/DEBUG = False/' config/settings.py && \
    poetry run python manage.py makemigrations && \
    poetry run python manage.py migrate && \
    poetry run python manage.py collectstatic --noinput

EXPOSE 8000

# Run Djagno
CMD ["poetry", "run", "python", "manage.py", "runserver", "0.0.0.0:8000"]

イメージが正しく作成されるかビルドしてみる

% docker build . -t django4.2:v$(date '+%Y.%-m.%-d')
% docker run --name django4.2 --rm -p8000:8000 django4.2:v$(date '+%Y.%-m.%-d')

確認: http://localhost:8000/api/memo/ にアクセスして確認

cssやjsが404でもOK

一旦イメージを削除しておく
% docker rmi django4.2:v$(date '+%Y.%-m.%-d')

Nginx用Dockerfileの作成

新規に作成するファイル

Django4.2-DRF
├── django ← 現在の階層
└── nginx           ← 新規作成
    └── Dockerfile  ← 新規作成
% cd ../
% mkdir nginx && cd nginx
% vi Dockerfile
nginx/Dockerfile
FROM nginx:latest

# Remove default log
RUN rm /var/log/nginx/access.log /var/log/nginx/error.log

# Create new logging pipe and point nginx logs to it
RUN ln -sf /dev/stdout /var/log/nginx/access.log && \
    ln -sf /dev/stdout /var/log/nginx/error.log

COPY default.conf /etc/nginx/conf.d/default.conf

EXPOSE 8080

Nginx用default.confの作成

Django4.2-DRF
├── django 
└── nginx  ← 現在の階層
    └── default.conf  ← 新規作成
% vi default.conf
nginx/default.conf
upstream django-upstream {
    server app:8000;
}

server {
    listen 8080;
    server_name localhost;

    access_log  /var/log/nginx/host.access.log  main;
    error_log   /var/log/nginx/error.log notice;

    location /static/ {
        alias /static/;
    }

    location / {
        proxy_pass http://django-upstream/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

docker-compose

新規に作成するファイル

Django4.2-DRF
└── nginx  ← 現在の階層
└── docker-compose.yml ← 新規作成
% cd ../
% vi docker-compose.yml
docker-compose.yml
version: '3'

services:
  app:
    container_name: app
    build: ./django/
    working_dir: /app
    volumes:
      - ./django:/app
      - static_volume:/app/static
    ports:
        - "8000:8000"
    command: >
      sh -c "poetry run python -m gunicorn config.asgi:application \
              --worker-class uvicorn.workers.UvicornWorker \
              --workers 4 \
              --bind 0.0.0.0:8000 \
              --max-requests 500 \
              --max-requests-jitter 200 \
              --timeout 300 \
              --graceful-timeout 300"

  web:
    container_name: web
    build: ./nginx/
    ports:
      - "8080:8080"
    volumes:
      - static_volume:/static
    depends_on:
      - app

volumes:
  static_volume:

起動してみる

% docker-compose up -d

↓のURLにアクセスできるか確認

Page URL
Django (Hello, world.) http://localhost:8080
Django administration http://localhost:8080/admin/
REST framewrk http://localhost:8080/api/
Swagger http://localhost:8080/api/schema/swagger-ui
Django REST framework JWT http://localhost:8080/api/token/

ここまでの変更をGitでコミット

% git add .
% git commit -m 'docker-compose対応'

docker-compose コマンド集

アクション コマンド
起動 docker-compose up -d
停止 docker-compose stop
再起動 docker-compose restart
ログを見る docker-compose logs
コンテナ削除 docker-compose down
コンテナとイメージ削除 docker-compose down --rmi all
15
11
1

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
15
11