29
25

More than 3 years have passed since last update.

Docker & Django API 実装 やってみよう

Last updated at Posted at 2020-07-23

はじめに

最近学習している Docker と Django REST framework を使って、Todo アプリの API 作成にチャレンジします、しました。
初心者奮闘記をここに記します。(ソースコードはこちら

到達目標

  • Docker を用いた開発環境構築
  • Django REST framework を使った簡易な API 作成

環境

macOS Catalina 10.15.2
docker desktop community 2.3.0.3
docker version Client: 19.03.8
docker version Server: 19.03.8

インプット

Django REST framework の概要

Django が Python で Webアプリケーションを作成するためフレームワークだったのに対し、Django REST Framework は Django を用いた WebAPI の開発をサポートしてくれるフレームワークのこと。Django REST framework を使えば RESTful な API バックエンドを簡単に構築することができます。
RESTful API に関してはこちらをご参照ください。

Docker の概要

Dockerはコンテナ仮想化技術を簡単に扱えるようにしたソフトウェアのこと。
従来の仮想環境構築に比べ、必要最小限のハードウェアのリソースでアプリケーションを利用できる、アプリケーションの起動が速い、OSやアプリケーションなどの環境が構築済みのコンテナイメージが用意されており、新たなアプリケーションの構築が簡単、といった点で優れているみたいです。
Docker に関してはこちらの記事が大変参考になりました。何度も読ませていただいてます。

Docker インストール

Docker公式 から Docker アカウントを作成し、Docker をインストールします。
インストールのページに進むと、Get StableGet Edge があるかと思います。
どうやらこれは更新頻度の違いらしく、Stable は3ヶ月に1度、Edge は毎月更新みたいです。
これは後からでも変更できるので、とりあえずどちらでも問題ないでしょう。

アカウント作成とインストールの詳細な手順はこちらの記事をご参照ください。

レッツ環境構築

Github でレポジトリ作成

Github にて適当なレポジトリを作成し、ローカルにクローンしましょう。
Github アカウントを持ち合わせていない方はこちらの記事を参考にアカウント作成をお願いします。
スクリーンショット 2020-07-20 7.43.38.png

ローカルPCにクローン

スクリーンショット 2020-07-20 7.58.28.png

URL をコピーし、ターミナルで git コマンドを入力します。

$ git clone https://github.com/Yi-Gaoqiao/todo-api.git

クローンしたレポジトリに移動し、中身を確認したらエディタを開きます。

$ cd todo-api/         # 作業ディレクトリに移動
$ ls                   # 中身を確認
LICENSE   README.md
$ code .               # vscode起動

Dockerfile の作成

Dockerfile(Dockerのイメージをビルドするための設定ファイル)を新規作成し、以下の通り記述します。
/todo-api/Dockerfile

FROM python:3.7-alpine                       # ベースとするDockerイメージを指定
LABEL architecture="Your Name"               # 構築担当者をラベル付け

ENV PYTHONUNBUFFERD 1                        # リアルタイムでログを見れるように環境変数を指定

COPY ./requirements.txt /requirements.txt    # ローカルのrequirements.txtをコンテナにコピー
RUN pip install -r /requirements.txt         # requirements.txtに従ってパッケージを一括でインストール

RUN mkdir /django-api                        # Djangoプロジェクトを置くディレクトリをコンテナ上に作成
WORKDIR /django-api                          # コンテナ上の作業ディレクトリを変更
COPY ./django-api /django-api                # ローカルのdjango-apiディレクトリをコンテナにコピー

RUN adduser -D user                          # アプリケーションを実行するためのユーザを作成する
USER user                                    # ユーザをrootから変更

ここで忘れないように、ローカルに django-api ディレクトリを作成しておきます。

requirements.txt の作成

必要なパッケージとパージョンをここに記述し、それらを一括でインストールします。
今回必要なパッケージは Django と djangorestframework です。各パッケージのバージョンは PyPI で検索しましょう。
ファイルの中身は以下の通りです。

/todo-api/requirements.txt

Django>=3.0.7,<3.1.0
djangorestframework>=3.11.0,<3.12.0

docker-compose.yml の作成

docker-compose.yml とはアプリケーションを動かすための処理を記述しているファイルです。
また、複数のコンテナを用いたアプリケーションの Docker イメージのビルドや各コンテナの起動・停止が簡単にできてしまう優れものです。
今回は app という1つのコンテナのみ起動させたいと思います。

/todo-api/docker-compose.yml

version: "3"

services:
  app:
    build:
      context: .
    ports:
      - "8000:8000"
    volumes:
      - ./django-api:/django-api
    command: >
      sh -c "python manage.py migrate &&
             python manage.py runserver 0.0.0.0:8000"

以下、それぞれのコマンドの説明です。もっともっと知りたい方はこちらの記事を参照ください。

  • services

    • この下にコンテナを定義。
  • app

    • コンテナの名前を指定。
  • build

    • Dockerfile が置いてあるディレクトリのパスを記述。
    • 配布されているイメージ(公式イメージなど)を取得する場合は指定不要。
  • ports

    • ポートマッピングを指定。"ホスト側:コンテナ側"。
  • volumes

    • ディレクトリマウント。
    • ホスト側からファイルを配置したり編集したりできるようマウントする。
  • command

    • コンテナ起動時に実行されるコマンド。サーバー起動時にマイグレーションするよう記述しています。

Dockerfile から Docker イメージのビルド

ターミナルで次のコマンドを実行します。

$ docker-compose build
Building app
...
Successfully built f624466b62a4
Successfully tagged todo-api_app:latest

コマンド実行後、Successfully tagged...のようになればばっちりです。
作成されたイメージは次のコマンドで確認できます。

$ docker images
REPOSITORY                       TAG                 IMAGE ID            CREATED             SIZE
todo-api_app                     latest              840de00a35b5        6 seconds ago       116MB

ここまでは問題なさそうですね。

Django プロジェクトの作成

ターミナルで次のコマンドを実行します。
コマンド実行後は先ほどの django-api ディレクトリの中に todo_project が作成されているか確認してください。

$ docker-compose run --rm app sh -c "django-admin.py startproject todo_project ."
  • --rm
    • コンテナを起動したりイメージを build したりしていると、どんどん使っていないコンテナやイメージなどが溜まっていき、ディスク容量を圧迫してしまいます。それを防ぐために、コマンド実行時にコンテナを削除してしまいます。
  • sh -c ""
    • ""内に実行するシェルスクリプトを記述します。このコマンドは必須ではないみたいですが、個人的にどこからどこまでが docker-compose のコマンドか明確にしたいので使うようにしています。

Django アプリの作成

アプリの作成は以下のコマンドを実行します。アプリ名はお好みでどうぞ(あたくしは core でいきます)。

$ docker-compose run --rm app sh -c "python manage.py startapp core"

カスタム User モデルの作成

Django 標準の User モデルではユーザー認証時、名前とパスワードが用いられます。
いや普通ユーザー登録ってなったら名前ではなくメールアドレスだろう、という皆々様のお声に応え、今回は特別に User モデルをカスタマイズします。
ごめんなさい嘘です。以下、Django 公式ドキュメントより

新しくプロジェクトを始める場合は、デフォルトの User で十分である場合でも、カスタムユーザーモデルを作成することを強く推奨します。このモデルはデフォルトのユーザーモデルと同様に動作しますが、必要に応じて将来的にカスタマイズすることができます:

ということで次のように記述します。

/django-api/core/models.py

from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.conf import settings    # あとで使います。

class UserManager(BaseUserManager):

    def create_user(self, email, password=None, **extra_fields):
        """Creates and saves a new user"""
        if not email:
            raise ValueError('User must have an email address')

        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)

        return user

    def create_superuser(self, email, password):
        """Creates and saves a new superuser"""
        user = self.create_user(email, password)
        user.is_staff = True
        user.is_superuser = True
        user.save(using=self._db)

        return user


class User(AbstractBaseUser, PermissionsMixin):
    """Custom user model that supports using email instead of username"""
    email = models.EmailField(max_length=255, unique=True)
    name = models.CharField(max_length=255)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

    objects = UserManager()

    USERNAME_FIELD = 'email'    # デフォルトは名前入力、今回はメールアドレスにカスタム
  • AbstractBaseUser
    • 最低限のフィールド及び機能以外は持ち合わせていないので、動作を柔軟に定義したい際に利用。
    • パーミッション関連の機能を持ち合わせていないので、パーミッションの機能を利用したい場合は、PermissionMixin を同時に継承しておく必要がある。
  • BaseUserManager
    • マネージャーをカスタマイズする際に継承。
    • マネージャーに関してはこちらをご参照ください。

アプリの追加登録と、カスタム User モデル利用の宣言

/django-api/todo-project/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'core',  # 追加
]

~~~~
 略
~~~~

STATIC_URL = '/static/'

AUTH_USER_MODEL = 'core.User'    # ファイル最下部に記述

注意点:一番初めの migrations(init(0001)) の前にカスタム User モデルの作成・登録を済ませないと後々不都合が生じるので、気をつけましょう。

マイグレーション

$ docker-compose run --rm app sh -c "python manage.py makemigrations core"
Migrations for 'core':
  core/migrations/0001_initial.py
    - Create model User

第一回コンテナ起動

さぁ、みなさんお待ちかね。コンテナ起動のお時間がやってまいりました。

$ docker-compose up
...
app_1  | Watching for file changes with StatReloade

dockerfile に記述したコマンドが実行され、コンテナが起動します。
起動がうまくいったら、http://127.0.0.1:8000/に行ってロケットの打ち上げをこの目に焼き付けましょう!
スクリーンショット 2020-07-22 7.10.16.png

管理者(superuser)の作成

docker-compose run --rm app sh -c "python manage.py createsuperuser"
Email: superuser@dummy.co.jp
Password: 
Password (again): 
Superuser created successfully.

任意のメールアドレスとパスワードを入力します(パスワードは画面に表示されません)。

管理画面にモデルを登録

先ほど作成した User モデルを管理画面で確認できるよう、admin.py に変更を加えます。
User モデルをカスタマイズしていたので、管理画面に表示させる項目もカスタマイズします。

/django-api/core/admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.utils.translation import gettext as _

from core import models


class UserAdmin(BaseUserAdmin):
    ordering = ['id']
    list_display = ['email', 'name']
    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        (_('Personal Info'), {'fields': ('name',)}),
        (
            _('Permissions'),
             {'fields': ('is_active', 'is_staff', 'is_superuser')}
        ),
        (_('Important dates'), {'fields': ('last_login',)})
    )

admin.site.register(models.User, UserAdmin)

ユーザーは id 順に並べられ、メールアドレスと名前が表示されるようにしています。また fieldsets では各ユーザーの詳細情報について表示する項目を指定しています。

  • BaseUserAdmin

    • デフォルトの UserAdmin を BaseUserAdmin としてインポートし、それを継承することでカスタマイズします。
  • gettext as _

    • gettext は多言語対応のために利用されます。慣習的に as _ としてインポートするみたいです。今回、言語設定に関してはデフォルトの英語のまま進めるので、翻訳に関して気になる方はこちらの記事をご参照ください。

ブラウザで確認

コードを見るだけではイメージ沸きにくいかと思うので、実際に管理画面にてどのように表示されるか見に行きましょう。
docker-compose up でコンテナを起動し、 http://127.0.0.1:8000/admin/に移動して先ほど作った superuser を確認します。
確認ができたら admin.py の記述内容と照らし合わせてみましょう。

http://127.0.0.1:8000/admin/core/user/1/change/
スクリーンショット 2020-07-22 8.16.38.png

User モデル対応 app の作成

わかりやすいように app の名前は user にします。

docker-compose run --rm app sh -c "python manage.py startapp user"

app を作成したら例の如く settings.py に登録します。

/django-api/todo-project/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',               # これから使います
    'rest_framework.authtoken',     # これから使います
    'core',
    'user',                         # 追加
]

Serializer の作成

シリアライズとは

クエリセットやモデルのインスタンスのような複雑なデータをJSON、XMLなどの出力可能なデータに書き出すこと。Django のモデルを異なるフォーマットに翻訳する、みたいな感じ。元の形式に復元する処理はデシリアライズと呼ぶ。(参考:Django REST framework 公式

serializers.py はデフォルトで用意されていないので、自分で作成します。
/django-api/user/serializers.py

from django.contrib.auth import get_user_model

from rest_framework import serializers


class UserSerializer(serializers.ModelSerializer):
    """Serializer for the user object"""

    class Meta:
        model = get_user_model()
        fields = ('email', 'password', 'name')
        extra_kwargs = {'password': {'write_only': True, 'min_length': 8}}

    def create(self, validated_data):
        """Create a new user with encrypted password and return it"""
        user = get_user_model().objects.create_user(**validated_data)

        return user
  • ModelSerializer

    • Django のモデルと紐づいています。モデルに基づいてフィールドとバリデータが自動的に Serializer にも適用されます。(Serializer はこれに限らず数種類用意されています。例えばただの Serializer は最も基本的なシリアライザで、対象が Django のモデルである必要はありません。このあと登場予定です。)
  • get_user_model()

    • get_user_model 関数は、そのプロジェクトで使用している User モデルを取得します。つまりデフォルトの User か、カスタムした User が返ります。汎用的な処理が書けるようになるので、ユーザーをインポートするときは get_user_model 関数を使うといいですね。

View の作成

View とは

View はクライアント(ブラウザなど)からのリクエストに応じ、どういった処理をするか(どの API を提供するか)を司っています。実装はめちゃんこシンプル。

/django-api/user/views.py

from rest_framework import generics

from user.serializers import UserSerializer


class CreateUserView(generics.CreateAPIView):
    """Create a new user in the system"""
    serializer_class = UserSerializer

serializer_class に先ほど作った UserSerializer を指定しています。
ここまでで Serializer と View の作成はひとまずおっけいです。ひとまずね。
urls.py を新規作成します。

/django-api/user/urls.py

from django.urls import path

from user import views


app_name = 'user'

urlpatterns = [
     path('create/', views.CreateUserView.as_view(), name='create'),
]
  • .as_view
    • こちらは View の条件を満たす関数を自動で実行してくれる優れものでございます。リクエストメソッド(GET, POST など)に応じた条件分岐の記述を省略してくれます。

続いて /user/urls.py を /todo_project/urls.py にルーティングします。

/django-api/todo_project/urls.py

from django.contrib import admin
from django.urls import path, include           # 追加

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/user/', include('user.urls')),    # 追加
]

ブラウザで確認

docker-compose up でコンテナを起動し、http://127.0.0.1:8000/api/user/create/ に行きましょう。
実際にメールアドレス、パスワード、名前を入力してユーザーを新規登録してみましょう。
スクリーンショット 2020-07-23 9.10.01.png

スクリーンショット 2020-07-23 9.11.45.png

スクリーンショット 2020-07-23 9.12.18.png

うまく登録されたようです。

トークン認証

トークン認証とは

いわゆる本人確認のようなもの。トークンはサーバーがユーザーに対して発行する認証情報のこと。
流れはこんな感じ ↓
スクリーンショット 2020-07-23 9.24.14.png

  1. クライアント(ここではブラウザ)がサーバーにユーザー情報(ここではメールアドレスとパスワード)を送る。
  2. サーバがユーザー情報を認証し、トークンを払い出す。
  3. 次回以降そのトークンをHTPPリクエストのヘッダーに含めることで、ユーザーが認証される。

例えば先ほど作成したユーザーがパスワードを変更したいとなった時、認証されたユーザー(本人)だけが変更できるように制御します。
まずはトークンの発行を実装します。

/django-api/user/serializers.py

from django.contrib.auth import get_user_model, authenticate  # 追加

from rest_framework import serializers


class UserSerializer(serializers.ModelSerializer):

# 追加
class AuthTokenSerializer(serializers.Serializer):
    """Serializer for the user authentication object"""
    email = serializers.CharField()
    password = serializers.CharField(
        style={'input_type': 'password'},
        trim_whitespace=False
    )

    def validate(self, attrs):
        """Validate and authenticate the user"""
        email = attrs.get('email')
        password = attrs.get('password')

        user = authenticate(
            request=self.context.get('request'),
            username=email,
            password=password
        )
        if not user:
            msg = ('Unable to authenticate with provided credentials')
            raise serializers.ValidationError(msg, code='authentication')

        attrs['user'] = user
        return attrs

トークンの発行に必要なユーザー情報の項目(メールアドレスとパスワード)を設定し、validate 関数で検証を行います。
入力された情報が一致しなかった場合にエラーメッセージを表示するようにしています。

/django-api/user/views.py

from rest_framework import generics
from rest_framework.authtoken.views import ObtainAuthToken        # 追加
from rest_framework.settings import api_settings                  # 追加

from user.serializers import UserSerializer, AuthTokenSerializer  # 追加


class CreateUserView(generics.CreateAPIView):

# 追加
class CreateTokenView(ObtainAuthToken):
    """Create a new auth token for user, restrict who can see Todo"""
    serializer_class = AuthTokenSerializer
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
  • renderer_classes
    • ブラウザ上で発行されたトークンを確認することができます。

/django-api/user/urls.py

from django.urls import path

from user import views


app_name = 'user'

urlpatterns = [
     path('create/', views.CreateUserView.as_view(), name='create'),
     path('token/', views.CreateTokenView.as_view(), name='token'),    # 追加
]

ブラウザで確認

もうわかりますよね。そうです、docker-compose up から http://127.0.0.1:8000/api/user/token/ に行きます。
先ほど登録したユーザーのトークンを発行してみましょう。
スクリーンショット 2020-07-23 10.11.38.png
スクリーンショット 2020-07-23 10.12.20.png
スクリーンショット 2020-07-23 10.12.53.png

不規則な謎の文字列が返ってきましたね。それ、トークンです。

ユーザー情報の更新

認証されたユーザーが自身の登録情報を更新できるようにします。

/django-api/user/serializers.py

class UserSerializer(serializers.ModelSerializer):
    """Serializer for the user object"""

    class Meta:
        model = get_user_model()
        fields = ('email', 'password', 'name')
        extra_kwargs = {'password': {'write_only': True, 'min_length': 8}}

    def create(self, validated_data):
        """Create a new user with encrypted password and return it"""
        user = get_user_model().objects.create_user(**validated_data)

        return user

    # 追加
    def update(self, instance, validated_data):
        """Update a user, setting the password correctly and return it"""
        password = validated_data.pop('password', None)
        user = super().update(instance, validated_data)

        if password:
            user.set_password(password)
            user.save()

        return user
  • .pop('password')
    • 更新前のパスワードを削除します。(無ければ None が返ってきます。)

/django-api/user/views.py

from rest_framework import generics, authentication, permissions  # 追加
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.settings import api_settings

from user.serializers import UserSerializer, AuthTokenSerializer


class CreateUserView(generics.CreateAPIView):

class CreateTokenView(ObtainAuthToken):


# 追加
class ManageUserView(generics.RetrieveUpdateAPIView):
    """Manage the authenticated user"""
    serializer_class = UserSerializer
    authentication_classes = (authentication.TokenAuthentication,)
    permission_classes = (permissions.IsAuthenticated,)

    def get_object(self):
        """Retrieve and return authentication user"""
        return self.request.user
  • authentication, permissions
    • 読んで字の如く、認証と許可を司ります。ここでは認証方法はトークン認証を利用し、そして認証されたユーザーのみ閲覧・編集を許可する、ということであります。(状況に応じて、認証されていないユーザーは閲覧のみ許可、といった制限もできます。)

/django-api/user/urls.py

from django.urls import path

from user import views


app_name = 'user'

urlpatterns = [
    path('create/', views.CreateUserView.as_view(), name='create'),
    path('token/', views.CreateTokenView.as_view(), name='token'),
    path('update/', views.ManageUserView.as_view(), name='update'),  # 追加
]

ブラウザで確認

docker-compose up から http://127.0.0.1:8000/api/user/update/ に行きます。
スクリーンショット 2020-07-23 10.33.14.png

そうです、実はこのままだとヘッダーにトークンが含まれていないため更新できません。
ここで chrome の ModHeader というツールを利用すると、ヘッダーにトークンを追加することができ、トークン有無での挙動の違いを検証できます。こちらは chrome ウェブストアから追加します。
追加後は画面右上のアイコンから Request header を追加し、Name に "Authorization" を、Value には "Token http://127.0.0.1:8000/api/user/token/ で発行されたトークン" を設定し、左にチェックを入れます。
スクリーンショット 2020-07-23 10.43.09.png

この状態でもう一度 http://127.0.0.1:8000/api/user/update/ にアクセスしてみてください。ユーザー情報が更新できるようになっています。
チェックを外すとまたしても更新できなくなります。
スクリーンショット 2020-07-23 10.51.06.png
スクリーンショット 2020-07-23 10.51.33.png
スクリーンショット 2020-07-23 10.51.49.png

お疲れ様でした。以上が User app に関わる記述です。
最後に Todo app を作成します。流れは User app とほぼ一緒ですので、ささっと行きます。

Todo API

モデル作成 → マイグレーション → app 作成 → app 登録 → Serializer → View → URL
以上の流れで参ります。

Todo モデルの作成

/django-api/core/models.py

# 最下部に追加
class Todo(models.Model):
    """Todo object"""
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE
    )

    title = models.CharField(max_length=100)
    content = models.CharField(max_length=255)
    created_at = models.DateField(auto_now_add=True)
    is_completed = models.BooleanField(default=False)

    def __str__(self):
        return self.title
  • settings.AUTH_USER_MODEL
    • 繰り返しになりますが今回は User モデルをカスタマイズしたので、User モデルの参照時はそれを参照するよう指定する必要があります。

/django-api/core/admin.py

admin.site.register(models.User, UserAdmin)
admin.site.register(models.Todo)  # 最下部に追加

マイグレーション

モデルに変更が加えられたときは忘れずにマイグレーションを。

$ docker-compose run --rm app sh -c "python manage.py makemigrations core"
Migrations for 'core':
  core/migrations/0002_todo.py
    - Create model Todo

Todo app の作成

$ docker-compose run --rm app sh -c "python manage.py startapp todo"

app 作成後は settings.py への登録も忘れずに。
/django-api/todo_project/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'rest_framework.authtoken',
    'core',
    'user',
    'todo',  # 追加
]

Serializer の作成

/django-api/todo/serializers.py(新規作成)

from rest_framework import serializers

from core.models import Todo
from user.serializers import UserSerializer

class TodoSerializer(serializers.ModelSerializer):
    """Serializer for Todo objects"""

    user = UserSerializer(read_only=True)

    class Meta:
        model = Todo
        fields = ('id', 'user', 'title', 'content', 'created_at', 'is_completed')
        read_only_fields = ('id', 'user',)

Todo モデルと紐づいている User モデルの user フィールドに関しては UserSerializer でシリアライズします。

View, Pagination の作成

/django-api/todo/views.py

from rest_framework import viewsets, generics, pagination, response
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated

from core.models import Todo

from todo import serializers


class TodoPagination(pagination.PageNumberPagination):
    """Get 2 Todo items in a page"""
    page_size = 2

    def get_paginated_response(self, data):
        return response.Response({
            'next': self.get_next_link(),
            'previous': self.get_previous_link(),
            'count': self.page.paginator.count,
            'total_pages': self.page.paginator.num_pages,
            'current_page': self.page.number,
            'results': data,
            'page_size': self.page_size,
            'range_first': (self.page.number * self.page_size) - (self.page_size) + 1,
            'range_last': min((self.page.number * self.page_size), self.page.paginator.count),
        })


class TodoViewSet(viewsets.ModelViewSet):
    """Handles creating, reading and updating todo items"""
    authentication_classes = (TokenAuthentication,)
    permission_classes = (IsAuthenticated,)
    serializer_class = serializers.TodoSerializer
    queryset = Todo.objects.order_by('-created_at')
    pagination_class = TodoPagination

    def perform_create(self, serializer):
        """Create a new Todo item"""
        serializer.save(user=self.request.user)
  • pagination.PageNumberPagination

    • デフォルトでは1ページで全件取得してしまうため、ページネーション機能を追加して1ページあたりの取得件数を制限します。
    • response.Response の中身は英字通りで、デフォルトよりちょっと見栄えがよくなります。
  • ModelViewSet

    • 基本的な APIView が備わっています。とても便利です。

以下、参考までに。

.list()              # 全件取得
.retrieve()          # 1件取得
.create()            # 作成
.update()            # 更新
.partial_update()    # 一部更新
.destroy()           # 削除

url の作成

/django-api/todo/urls.py(新規作成)

from django.urls import path, include
from rest_framework.routers import DefaultRouter

from todo import views


router = DefaultRouter()
router.register('todo', views.TodoViewSet)

app_name = 'todo'

urlpatterns = [
    path('', include(router.urls))
]
  • DefaultRouter
    • Router を登録できるのは ViewSet に限ります。user app で作成した generics の view とか登録しようもんならちゃんとエラー発生します。
    • Router はめちゃめちゃ便利で、詳細な API(/todo/1/ とか /todo/4/)を自動的に付加して URL 登録してくれます。わざわざ /todo/int:pk/ みたいなこと書かなくていいんです。すごい。

/django-api/todo_project/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('api/user/', include('user.urls')),

path('api/', include('todo.urls')), # 追加
]

これで本チャレンジの全工程が終了しました。
最後にブラウザで確認しましょう。

ブラウザで確認(最終回)

docker-compose up から http://127.0.0.1:8000/api/ に行きます。
"todo": "http://127.0.0.1:8000/api/todo/" に飛んでみましょう。
(繰り返しになりますが、認証されていないユーザーは閲覧すらできなくなっています。)

スクリーンショット 2020-07-23 11.59.04.png

3つほど Todo を入力してみましょう。
スクリーンショット 2020-07-23 11.56.46.png

入力した Todo もおしゃれなページネーションもばっちり反映されています。
我ながら上出来です。
スクリーンショット 2020-07-23 11.57.56.png

まとめ

今回は Docker で開発環境を構築し、Django REST framework で Todo アプリの API 実装にチャレンジしました。
初めて触れるものが多かったので完全理解とまではいきませんが、自分で調べながら完成させることができたので満足でございます。

参考

本とプログラミングと時々ミニブタ
Django Brothers BLOG
さくらのナレッジ Docker入門
Narito Blog
Django 公式ドキュメント
Django REST framework 公式
slideship Django REST framework 実践入門
Dockerとはどういったものなのか、めちゃくちゃ丁寧に説明してみる
Build a Backend REST API with Python & Django – Beginner

29
25
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
29
25