Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
10
Help us understand the problem. What is going on with this article?
@mykysyk@github

Django + REST framework + Swagger + JWT + docker-compose で開発環境構築

More than 1 year has passed since last update.

目的

MacのDocker上に開発サーバを構築し、下記URLにブラウザアクセスが可能な状態にする。
構築後は下記URLにアクセスできれば成功

Page URL
Django (Hello, world.) http://localhost:8000
Django administration http://localhost:8000/admin/
REST framewrk http://localhost:8000/api/
Swagger http://localhost:8000/swagger/

環境

MacOSX: Mojave
Python: 3
Django: 2.2.12
Django REST framework: 3.11.0
swagger: 2.2.0

以下、環境構築履歴

Django 環境構築履歴

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

$ mkdir django-docker
$ cd django-docker/
$ echo '# django-docker' > README.md
$ (echo '*.pyc'
   echo '__pycache__'
   echo 'db.sqlite3'
   echo 'venv/*'
  ) > .gitignore
$ git init
$ git add .
$ git commit -m 'first commit'

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

django-docker
├── .git
├── .gitignore
└── README.md

仮想環境の作成

$ python3 -m venv venv
$ source ./venv/bin/activate
$ pip install --upgrade pip

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

django-docker
├── .git
├── .gitignore
├── README.md
└── venv # virtualenv用に新規作成

Djangoのインストール

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

$ pip install Django==2.2.12
$ python -m django --version
2.2.12

プロジェクト作成

$ django-admin startproject config .
$ git add config/ manage.py
$ git commit -m 'project 作成'

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

django-docker
├── .git
├── .gitignore
├── README.md
├── config           # プロジェクトが作成された
│   ├── __init__.py  # プロジェクトが作成された
│   ├── settings.py  # プロジェクトが作成された
│   ├── urls.py      # プロジェクトが作成された
│   └── wsgi.py      # プロジェクトが作成された
├── manage.py        # プロジェクトが作成された
└── venv

sample_app アプリケーション作成

$ python manage.py startapp sample_app
$ git add sample_app
$ git commit -m 'sample_app 作成'

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

django-docker
├── .git
├── .gitignore
├── README.md
├── config
│   ├── __init__.py
│   ├── __pycache__
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── sample_app       # アプリケーションが作成された
│   ├── __init__.py  # アプリケーションが作成された
│   ├── admin.py     # アプリケーションが作成された
│   ├── apps.py      # アプリケーションが作成された
│   ├── migrations   # アプリケーションが作成された
│   ├── models.py    # アプリケーションが作成された
│   ├── tests.py     # アプリケーションが作成された
│   └── views.py     # アプリケーションが作成された
└── venv

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

編集するファイル

django-docker
└── config
    └── settings.py ← これ
$ vi config/settings.py

既存ファイルとの差分

config/settings.py
$ git diff config/settings.py

diff --git a/config/settings.py b/config/settings.py
index 14a482e..0b0f505 100644
--- a/config/settings.py
+++ b/config/settings.py
@@ -31,6 +31,7 @@ ALLOWED_HOSTS = []
 # Application definition

 INSTALLED_APPS = [
+    'sample_app',
     'django.contrib.admin',
     'django.contrib.auth',
     'django.contrib.contenttypes',
$ git add config/settings.py
$ git commit -m 'sample_app のアプリケーション登録'

ビュー作成

編集するファイル

django-docker
└── sample_app
    └── views.py ← これ
$ vi sample_app/views.py
sample_app/views.py
from django.shortcuts import render
from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world.")
$ git add sample_app/views.py
$ git commit -m 'テストビューの作成'

アプリ用URLConfの新規作成

編集するファイル

django-docker
└── sample_app
    └── urls.py ← これ新規作成
$ vi sample_app/urls.py
sample_app/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]
$ git add sample_app/urls.py
$ git commit -m 'アプリ用URLConfの新規作成'

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

django-docker
├── .git
├── .gitignore
├── README.md
├── config
│   ├── __init__.py
│   ├── __pycache__
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── sample_app
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   ├── models.py
│   ├── tests.py
│   ├── urls.py # 新規作成
│   └── views.py
└── venv

ルート用URLConfの編集

編集するファイル

django-docker
└── config
    └── urls.py ← これ
$ vi config/urls.py

既存ファイルとの差分

config/urls.py
$ git diff config/urls.py

diff --git a/config/urls.py b/config/urls.py
index 3a96dfd..a72a1f9 100644
--- a/config/urls.py
+++ b/config/urls.py
@@ -14,8 +14,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),
 ]
$ git add config/urls.py
$ git commit -m 'ルート用URLConfの編集'

開発サーバーを起動して http://localhost:8000 にアクセスし「Hello, world.」が出ればOK

$ python manage.py runserver

Django REST framework

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

Django REST framework のインストール

$ pip install djangorestframework django-filter

モデルの定義

編集するファイル

django-docker
└── sample_app
    └── models.py ← これ
$ vi sample_app/models.py
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)
$ git add sample_app/models.py
$ git commit -m 'sample_app のモデル定義'

データベース構築

$ python manage.py makemigrations

Migrations for 'sample_app':
  sample_app/migrations/0001_initial.py
    - Create model Memo
$ python manage.py migrate

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sample_app, 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 sample_app.0001_initial... OK
  Applying sessions.0001_initial... OK
$ git add sample_app/migrations/0001_initial.py
$ git commit -m 'python manage.py makemigrations'

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

django-docker
├── .git
├── .gitignore
├── README.md
├── config
│   ├── __init__.py
│   ├── __pycache__
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── db.sqlite3               # makemigrationsによって作成
├── manage.py
├── sample_app
│   ├── __init__.py
│   ├── __pycache__
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   ├── 0001_initial.py # makemigrationsによって作成
│   │   ├── __init__.py     # makemigrationsによって作成
│   │   └── __pycache__     # makemigrationsによって作成
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
└── venv

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

編集するファイル

django-docker
└── sample_app
    └── admin.py ← これ
$ vi sample_app/admin.py
sample_app/admin.py
from django.contrib import admin
from .models import Memo

@admin.register(Memo)
class MemoAdmin(admin.ModelAdmin):
    pass
$ git add sample_app/admin.py
$ git commit -m '管理画面にsample_appのモデルを追加する'

管理画面に確認用としてログインできるユーザーとパスワード設定

$ python manage.py createsuperuser
$ python manage.py runserver

http://localhost:8000/admin にアクセスしてログインできるのを確認
login画面

ログイン後

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

編集するファイル

django-docker
└── config
    └── settings.py ← これ
$ vi config/settings.py

既存ファイルとの差分

config/settings.py
$ git diff config/settings.py

diff --git a/config/settings.py b/config/settings.py
index 0b0f505..3d3ec95 100644
--- a/config/settings.py
+++ b/config/settings.py
@@ -31,6 +31,7 @@ ALLOWED_HOSTS = []
 # Application definition

 INSTALLED_APPS = [
+    'rest_framework',
     'sample_app',
     'django.contrib.admin',
     'django.contrib.auth',
$ git add config/settings.py
$ git commit -m 'Djagno REST Framework のアプリケーション登録'

Serializerの定義

新規作成するファイル

django-docker
└── 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__'
$ git add sample_app/serializer.py
$ git commit -m 'Serializerの定義'

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

django-docker
├── .git
├── .gitignore
├── README.md
├── config
│   ├── __init__.py
│   ├── __pycache__
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── db.sqlite3
├── manage.py
├── sample_app
│   ├── __init__.py
│   ├── __pycache__
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   ├── models.py
│   ├── serializer.py # 新規で作成
│   ├── tests.py
│   ├── urls.py
│   └── views.py
└── venv

ViewSetの追加

編集するファイル

django-docker
└── sample_app
    └── views.py ← これ
$ vi sample_app/views.py
sample_app/views.py
import django_filters

from django.shortcuts import render
from django.http import HttpResponse
from rest_framework import viewsets, filters
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
$ git add sample_app/views.py
$ git commit -m 'ViewSetの定義'

アプリ用URLConfの編集

REST frameworkへの設定を組み込む

編集するファイル

django-docker
└── sample_app
    └── urls.py ← これ
$ vi sample_app/urls.py

既存ファイルとの差分

sample_app/urls.py
$ git diff sample_app/urls.py

diff --git a/sample_project/sample_app/urls.py b/sample_project/sample_app/urls.py
index 88a9cac..c4608a2 100644
--- a/sample_project/sample_app/urls.py
+++ b/sample_project/sample_app/urls.py
@@ -1,7 +1,13 @@
 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)
$ git add sample_app/urls.py
$ git commit -m 'REST frameworkの設定をアプリ用URLConfに組み込む'

ルート用URLConfの編集

編集するファイル

django-docker
└── config
    └── urls.py ← これ
$ vi config/urls.py

既存ファイルとの差分

sample_project/urls.py
$ git diff sample_project/urls.py

diff --git a/sample_project/sample_project/urls.py b/sample_project/sample_project/urls.py
index 8a17227..ab1c4f5 100644
--- a/sample_project/sample_project/urls.py
+++ b/sample_project/sample_project/urls.py
@@ -15,8 +15,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)),
 ]
$ git add config/urls.py
$ git commit -m 'REST frameworkの設定をルート用URLConfに組み込む'
$ python manage.py runserver

http://localhost:8000/api/ にアクセスできるか確認

Django REST Framework

Django REST Swagger

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

Django REST Swagger のインストール

$ pip install django-rest-swagger

Django REST Swagger の有効化

編集するファイル

django-docker
└── config
    └── settings.py ← これ
$ vi config/settings.py

※ DRF 3.10.0 から AttributeError: 'AutoSchema' object has no attribute 'get_link' が発生するのでDEFAULT_SCHEMA_CLASSで対策する必要がある。

config/settings.py
$ $ git diff config/settings.py
diff --git a/config/settings.py b/config/settings.py
index 39ec00e..c394124 100644
--- a/config/settings.py
+++ b/config/settings.py
@@ -31,6 +31,7 @@ ALLOWED_HOSTS = []
 # Application definition

 INSTALLED_APPS = [
+    'rest_framework_swagger',
     'rest_framework',
     'sample_app',
     'django.contrib.admin',
@@ -120,3 +121,5 @@ USE_TZ = True
 # https://docs.djangoproject.com/en/2.2/howto/static-files/

 STATIC_URL = '/static/'
+
+REST_FRAMEWORK = {'DEFAULT_SCHEMA_CLASS':'rest_framework.schemas.coreapi.AutoSchema' }
$ git add config/settings.py
$ git commit -m 'swaggerの有効化'

ルート用URLConfの編集

django-docker
└── config
    └── urls.py ← これ
$ vi config/urls.py

既存ファイルとの差分

config/urls.py
$ git diff config/urls.py

diff --git a/config/urls.py b/config/urls.py
index 672ac1a..eefd987 100644
--- a/config/urls.py
+++ b/config/urls.py
@@ -16,9 +16,13 @@ Including another URLconf
 from django.contrib import admin
 from django.urls import path, include
 from sample_app.urls import router as sample_app_router
+from rest_framework_swagger.views import get_swagger_view
+
+schema_view = get_swagger_view(title='API Lists')

 urlpatterns = [
     path('', include('sample_app.urls')),
     path('admin/', admin.site.urls),
     path('api/', include(sample_app_router.urls)),
+    path('swagger/', schema_view),
 ]
$ git add config/urls.py
$ git commit -m 'swaggerの設定をルート用URLConfに組み込む'
$ python manage.py runserver

http://localhost:8000/swagger/ にアクセスしてswaggerが表示されているか確認
Django REST Swagger

JWT

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

djangorestframework-jwt のインストール

$ pip install djangorestframework-jwt

djangorestframework-jwt の有効化

編集するファイル

django-docker
└── config
    └── settings.py ← これ
$ vi config/settings.py
config/settings.py
$ git diff config/settings.py

diff --git a/config/settings.py b/config/settings.py
index 10fb176..41afa86 100644
--- a/config/settings.py
+++ b/config/settings.py
@@ -123,4 +123,12 @@ USE_TZ = True
 STATIC_URL = '/static/'

 # https://github.com/encode/django-rest-framework/issues/6809
-REST_FRAMEWORK = {'DEFAULT_SCHEMA_CLASS':'rest_framework.schemas.coreapi.AutoSchema' }
+REST_FRAMEWORK = {
+        'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
+        'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
+        'DEFAULT_AUTHENTICATION_CLASSES': (
+            'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
+            'rest_framework.authentication.SessionAuthentication',
+            'rest_framework.authentication.BasicAuthentication',
+        ),
+}
$ git add config/settings.py
$ git commit -m 'JWTの有効化'

ルート用URLConfの編集

django-docker
└── config
    └── urls.py ← これ
$ vi config/urls.py

既存ファイルとの差分

config/urls.py
$ git diff config/urls.py

diff --git a/config/urls.py b/config/urls.py
index eefd987..f3e2df9 100644
--- a/config/urls.py
+++ b/config/urls.py
@@ -17,6 +17,7 @@ from django.contrib import admin
 from django.urls import path, include
 from sample_app.urls import router as sample_app_router
 from rest_framework_swagger.views import get_swagger_view
+from rest_framework_jwt.views import obtain_jwt_token

 schema_view = get_swagger_view(title='API Lists')

@@ -25,4 +26,5 @@ urlpatterns = [
     path('admin/', admin.site.urls),
     path('api/', include(sample_app_router.urls)),
     path('swagger/', schema_view),
+    path('api-auth/', obtain_jwt_token),
 ]
$ git add config/urls.py
$ git commit -m 'JWTの設定をルート用URLConfに組み込む'
$ python manage.py runserver

確認

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

$ curl -XGET http://127.0.0.1:8000/api/memo/

{"detail":"Authentication credentials were not provided."}

トークンの発行

$ USERNAME='admin'
$ PASSWORD='userpassword'
$ curl http://127.0.0.1:8000/api-auth/ -d "username=${USERNAME}&password=${PASSWORD}"

{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTg3MTM3OTc1LCJlbWFpbCI6IiJ9.uBKhe8ASSPaUXPVjMxxiuZsT4AIWq_8Nca9Hdk4NWRc"}

トークンありでGETリクエスト

$ JWT=$(curl -s -XPOST http://127.0.0.1:8000/api-auth/ -d "username=${USERNAME}&password=${PASSWORD}" | jq -r .token)
$ curl -XGET -H "Authorization: JWT ${JWT}" http://127.0.0.1:8000/api/memo/

[]

POSTしてデータ登録

$ curl -XPOST -H "Authorization: JWT ${JWT}" http://127.0.0.1:8000/api/memo/ -d 'title=a&memo=a'

{"id":1,"title":"a","memo":"a"}

POSTデータの確認

$ curl -XGET -H "Authorization: JWT ${JWT}" http://127.0.0.1:8000/api/memo/

[{"id":1,"title":"a","memo":"a"}]

Dockerの構築

新規に作成するファイル

django-docker
├── Dockerfile       ← 新規作成
└── requirements.txt ← 新規作成
$ vi Dockerfile
Dockerfile
FROM python:3-alpine
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
$ pip freeze > requirements.txt
requirements.txt
certifi==2020.4.5.1
chardet==3.0.4
coreapi==2.3.3
coreschema==0.0.4
Django==2.2.12
django-filter==2.2.0
django-rest-swagger==2.2.0
djangorestframework==3.11.0
djangorestframework-jwt==1.11.0
idna==2.9
itypes==1.1.0
Jinja2==2.11.2
MarkupSafe==1.1.1
openapi-codec==1.3.2
PyJWT==1.7.1
pytz==2019.3
requests==2.23.0
simplejson==3.17.0
sqlparse==0.3.1
uritemplate==3.0.1
urllib3==1.25.9

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

$ docker build . -t django-docker

問題なくビルドしたら一旦削除しておく

$ docker rmi django-docker

docker-compose

新規に作成するファイル

django-docker ← docker-composeコマンドはこのディレクトリで実行する
└── docker-compose.yml ← 新規作成
$ cd ../
$ vi docker-compose.yml
docker-compose.yml
version: '3'

services:
  django-docker:
    container_name: django-docker-compose
    build: .
    working_dir: /code
    volumes:
        - .:/code
    ports:
        - "8000:8000"
    command: >
      sh -c "apk update &&
             apk add bash &&
             apk add git &&
             python manage.py makemigrations &&
             python manage.py migrate &&
             python manage.py runserver 0.0.0.0:8000"
$ git add docker-compose.yml Dockerfile requirements.txt
$ git commit -m 'docker-compose対応'
$ docker-compose up -d

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

Page URL
Django (Hello, world.) http://localhost:8000
Django administration http://localhost:8000/admin/
REST framewrk http://localhost:8000/api/
Swagger http://localhost:8000/swagger/

github

できあがったソースはこちらで下記コマンドでいつでも開発環境を構築できる

$ git clone https://github.com/mykysyk/django-docker.git
$ cd django-docker
$ docker-compose up -d
$ docker exec -i -t django-docker-compose bash
bash-5.0# python manage.py createsuperuser

docker-compose コマンド集

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

参考

https://qiita.com/homines22/items/2730d26e932554b6fb58
https://www.django-rest-framework.org/tutorial/quickstart/
https://django-rest-swagger.readthedocs.io/en/latest/
https://docs.docker.com/compose/django/
https://jpadilla.github.io/django-rest-framework-jwt/

10
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
10
Help us understand the problem. What is going on with this article?