🚾

Django REST FrameworkでAPIを作ってdocker-composeで動かす

2023/07/09に公開

はじめに

ECSの練習をするためのコンテナがほしかったので、とりあえず動くものを作成しました。
使用したフレームワークはpythonのDjangoで、簡単にAPIを作成するためにDjango REST Frameworkも活用しています。

https://www.django-rest-framework.org/

注意

とりあえず動かすことに注力しました。
セキュリティ等はまったく考慮できていません。

環境

ubuntu 22.04.2
docker 24.0.4
docker compose 2.19.1
requirements.txt

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アプリケーションを追加

python
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に変換できるようにします。

serializer.py
from rest_framework import serializers
from .models import ToDo

class ToDoSerializer(serializers.ModelSerializer):
    class Meta:
        model = ToDo
        fields = ('id', 'title', 'description', 'completed')

https://qiita.com/AJIKING/items/29327b7f7c46e2245505
https://www.django-rest-framework.org/api-guide/viewsets/

View,URLを編集する

ModelViewSetを使用することで、list, retrieve, create,update(),partial_update,destroyが使えるようになるみたいです。
楽してCRUDが使えるということでしょうか。
https://www.django-rest-framework.org/api-guide/viewsets/

また各アプリの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に載せるときにマイグレーションはどうしよう...マイグレーション実行用のタスクを作る?
考えましょう。

参考

公式ドキュメント
https://genchan.net/it/virtualization/docker/3951/
https://chigusa-web.com/blog/django-rest-framework/

Discussion