Django REST FrameworkでAPIを作ってdocker-composeで動かす
はじめに
ECSの練習をするためのコンテナがほしかったので、とりあえず動くものを作成しました。
使用したフレームワークはpythonのDjango
で、簡単にAPIを作成するためにDjango REST Framework
も活用しています。
注意
とりあえず動かすことに注力しました。
セキュリティ等はまったく考慮できていません。
環境
ubuntu 22.04.2
docker 24.0.4
docker compose 2.19.1
requirements.txt
Django == 4.1
psycopg2 == 2.9.6
djangorestframework == 3.14.0
DBはpostgresqlで、djangoとともにdocker composeで管理します
イメージ図
作成するアプリ
HTTPメソッドでCreate, Read, Update, Delete(CRUD)操作が可能なtodo管理アプリを想定します。
- POST:todo追加(Create)
- GET:todo一覧表示(Read)
- PUT:todo編集(Update)
- DELETE:todo削除(Delete)
検証の進め方
- コンテナ等各種ファイルを用意
- Djangoプロジェクト・アプリケーション作成
- DBマイグレーション
- コード編集
- 動作確認
検証
事前準備
最終的なフォルダ構成はこちらです。
事前にDockerfile,docker-compose.yaml,entrypoint.sh,requirements.txtを作成します。
.
├── Dockerfile
├── docker-compose.yaml
├── entrypoint.sh
├── manage.py # プロジェクト作成時に自動生成
├── requirements.txt
├── todo # プロジェクト
└── todoapp # アプリケーション
準備するファイル群
Dockerfile
FROM python:3.11.4-bookworm
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/
RUN apt-get update && apt-get install -y netcat-traditional
COPY ./entrypoint.sh /usr/src/app/entrypoint.sh
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
requirements.txt
Django == 4.1
psycopg2 == 2.9.6
djangorestframework == 3.14.0
docker-compose.yaml
version: '3.9'
services:
db:
image: postgres
ports:
- "5432:5432"
environment:
- POSTGRES_PASSWORD=postgres
web:
environment:
- DATABASE=postgres
- DB_HOST=db
- DB_PORT=5432
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- db
entrypoint.sh
#!/bin/sh
if [ "$DATABASE" = "postgres" ]
then
echo "Waiting for postgres..."
while ! nc -z $DB_HOST $DB_PORT; do
sleep 0.1
done
echo "PostgreSQL started"
fi
exec "$@"
entrypoint.shではDjangoコンテナ起動時、データベースが完全に立ち上がるのを待っています。
プロジェクト・アプリケーション作成
# プロジェクト作成
docker compose run web django-admin startproject todo .
# アプリケーションを作る
docker compose run web python manage.py startapp todoapp
マイグレーションまで
データベース定義を作成・適用します。
モデル作成
from django.db import models
class ToDo(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
completed = models.BooleanField(default=False)
settings.py変更
データベース情報の編集
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': 'postgres',
'HOST': 'db',
'PORT': '5432',
}
}
todo
アプリケーションを追加
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
+ 'todoapp',
]
検証のためすべてのアクセスを許可
ALLOWED_HOSTS = ["*"]
マイグレーション
# migrationファイル作成
docker compose run web python manage.py makemigrations todoapp
# migrate
docker compose run web python manage.py migrate
Serializerの定義
データをjsonに変換できるようにします。
from rest_framework import serializers
from .models import ToDo
class ToDoSerializer(serializers.ModelSerializer):
class Meta:
model = ToDo
fields = ('id', 'title', 'description', 'completed')
View,URLを編集する
ModelViewSetを使用することで、list, retrieve, create,update(),partial_update,destroy
が使えるようになるみたいです。
楽してCRUDが使えるということでしょうか。
また各アプリのURLはprojectのurls.pyへ集約するようにしています。
views.py
from rest_framework import viewsets
from .models import ToDo
from .serializers import ToDoSerializer
class ToDoViewSet(viewsets.ModelViewSet):
queryset = ToDo.objects.all()
serializer_class = ToDoSerializer
urls.py(app)
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ToDoViewSet
router = DefaultRouter()
router.register(r'todos', ToDoViewSet)
urlpatterns = [
path('', include(router.urls)),
]
urls.py(project)
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('todoapp.urls')),
]
動作確認
GET
一覧表示
$ curl -s -X GET http://localhost:8000/api/todos/ | jq
[
{
"id": 1,
"title": "Test todo",
"description": "This is a test",
"completed": false
}
]
POST
新規追加
$ curl -X POST -H "Content-Type: application/json" -d '{"title":"Test2", "description":"Test2"}' http://localhost:8000/api/todos/
{"id":2,"title":"Test2","description":"Test2","completed":false}
PUT
更新
$ curl -X PUT -H "Content-Type: application/json" -d '{"title":"update", "description":"update"}' http://localhost:8000/api/todos/4/
DELETE
削除
$ curl -X DELETE http://localhost:8000/api/todos/1/
終わりに
とりあえず動きました。
ECSに載せるときにマイグレーションはどうしよう...マイグレーション実行用のタスクを作る?
考えましょう。
参考
公式ドキュメント
Discussion