タスク管理アプリを作っていく

はじめに

PythonのDjangoでWEBアプリ開発を行いたい。

そこでまずは、タスク管理アプリを作成していく。

記事ベースで進めていくので、見届けて欲しい。

アプリのコンセプト

以下の2点が達成できればいい(今回は)

  • ユーザーがタスクを追加・編集・削除できる
  • タスクの完了・未完了を管理できる

Djangoプロジェクトを準備する

WEBアプリを作るのに必要なPython製フレームワークのDjangoを使用していく。

pythonの仮想環境はvenvを準備してある。

django_todoというディレクトリを作成した。

venvは有効になっている。

(venv) Mac:python shibatahiroshitaka$ 

DjangoとDjango REST Frameworkをインストールしていく。

(venv) Mac:python shibatahiroshitaka$ pip install django djangorestframework
Collecting django
  Downloading Django-4.2.19-py3-none-any.whl (8.0 MB)
     |████████████████████████████████| 8.0 MB 6.2 MB/s 
Collecting djangorestframework
  Downloading djangorestframework-3.15.2-py3-none-any.whl (1.1 MB)
     |████████████████████████████████| 1.1 MB 31.0 MB/s 
Collecting sqlparse>=0.3.1
  Downloading sqlparse-0.5.3-py3-none-any.whl (44 kB)
     |████████████████████████████████| 44 kB 5.9 MB/s 
Collecting backports.zoneinfo; python_version < "3.9"
  Downloading backports.zoneinfo-0.2.1.tar.gz (74 kB)
     |████████████████████████████████| 74 kB 4.5 MB/s 
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
    Preparing wheel metadata ... done
Collecting asgiref<4,>=3.6.0
  Downloading asgiref-3.8.1-py3-none-any.whl (23 kB)
Collecting typing-extensions>=4; python_version < "3.11"
  Downloading typing_extensions-4.12.2-py3-none-any.whl (37 kB)
Building wheels for collected packages: backports.zoneinfo
  Building wheel for backports.zoneinfo (PEP 517) ... done
  Created wheel for backports.zoneinfo: filename=backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl size=49348 sha256=a69943f32191e40aeab498155fa1a1db3aa85ccb06f25cd8634bceb94461c074
  Stored in directory: /Users/shibatahiroshitaka/Library/Caches/pip/wheels/c7/de/cc/c405827ed64f81b56142f1e0075a970b2731b00d21983d54fb
Successfully built backports.zoneinfo
Installing collected packages: sqlparse, backports.zoneinfo, typing-extensions, asgiref, django, djangorestframework
Successfully installed asgiref-3.8.1 backports.zoneinfo-0.2.1 django-4.2.19 djangorestframework-3.15.2 sqlparse-0.5.3 typing-extensions-4.12.2
WARNING: You are using pip version 20.2.3; however, version 25.0.1 is available.
You should consider upgrading via the '/Users/shibatahiroshitaka/Downloads/python/venv/bin/python3 -m pip install --upgrade pip' command.

関連するライブラリも含めて、djangoとdjangorestframeworkをインストールできたようだ。

djangorestframeworkについて説明しよう。

djangoはもともとWEBアプリを構築するためのフレームワークだが、DRF(Django REST Framework)を使うことで、フロントエンド(Vue.js、Reactなど)やモバイルアプリ(iOS、Android)と連携できるAPI(Application Programing Interface)を簡単に構築できる。

次にDjangoをセットアップして、開発環境を整える。

先ほど作成した「django_todo」というフォルダは削除する。

なぜなら、Djangoでは以下のようにしてプロジェクトフォルダを作成するというお作法があるからだ。

django-admin startproject <プロジェクト名>

以下のようにしてプロジェクトフォルダを作成した。

(venv) Mac:python shibatahiroshitaka$ django-admin startproject django_todo

django_todoに移動して、最初の開発環境を作成した。

(venv) Mac:python shibatahiroshitaka$ cd django_todo
(venv) Mac:django_todo shibatahiroshitaka$ python manage.py startapp myapp

【簡単なAPIを実装】モデルの作成

APIとは、データをやり取りするための仕組みのこと。

モデルは、データベースの設計の役割を担っている。

シリアライザーは、モデルのデータをJSONに変換する。

まずは、models.pyにタスク管理のモデルを書く。

デフォルトは以下のようになっているので、4行目以降に書いていく。

まずは、models.Modelを継承することで、DjangoのORM(Object-Relational Mapping)を利用できるようにする。Taskというクラス名。

class Task(models.Model):

タイトル、説明、完了状態の3カラムを作成するためのモデルを書いていく。

まずはタイトル。100文字までの文字列のカラムを作成する。

  title = models.CharField(max_length=100)

次に説明。空をOKにする。

  description = models.TextField(blank=True)

最後に完了状態。デフォルトをFalseに設定した。

  completed = models.BooleanField(default=False)

これでカラムの定義は完了。

from django.db import models

# Create your models here.
class Task(models.Model):
  title = models.CharField(max_length=100)
  description = models.TextField(blank=True)
  completed = models.BooleanField(default=False)

最後に、このオブジェクトを取得する時に、タイトルが返されるようにする。

  def __str__(self):
    return self.title

次に、マイグレーションファイルの作成とマイグレーションを行う。

python manage.pyに続けて、makemigrationsでマイグレーションファイルが作成される。マイグレーションファイルの内容がDBに反映される。

(venv) Mac:django_todo shibatahiroshitaka$ python manage.py makemigrations
No changes detected
(venv) Mac:django_todo shibatahiroshitaka$ 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

makemigrationsをした時に、migrainonsフォルダとdb.sqlite3が作成された。

DBの中身を確認してみよう。

Djangoのshellにアクセスする。

(venv) Mac:django_todo shibatahiroshitaka$ python manage.py shell
Python 3.8.9 (default, Oct 26 2021, 07:25:54) 
[Clang 13.0.0 (clang-1300.0.29.30)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>

先ほど作成したTaskクラスをインポートしようとするとエラーが発生した。

>>> from myapp.models import Task
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/shibatahiroshitaka/Downloads/python/django_todo/myapp/models.py", line 4, in <module>
    class Task(models.Model):
  File "/Users/shibatahiroshitaka/Downloads/python/venv/lib/python3.8/site-packages/django/db/models/base.py", line 134, in __new__
    raise RuntimeError(
RuntimeError: Model class myapp.models.Task doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.

このエラー原因は、Djangoにmyappが認識されていないということ。

INSTALLED_APPSという場所にmyappを追加してあげればいい。

settings.pyにINSTALLED_APPSがある。ここの一番最後に先ほど作成したmyappを追記する。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp'
]

そしたらマイグレーションを再実行する。

シェルは一度抜ける。

>>> exit()
(venv) Mac:django_todo shibatahiroshitaka$ python manage.py makemigrations
Migrations for 'myapp':
  myapp/migrations/0001_initial.py
    - Create model Task
(venv) Mac:django_todo shibatahiroshitaka$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, myapp, sessions
Running migrations:
  Applying myapp.0001_initial... OK

もう一度シェルを起動してTaskクラスを読み込んだら、今度はエラーが起こらなかった。

(venv) Mac:django_todo shibatahiroshitaka$ python manage.py shell
Python 3.8.9 (default, Oct 26 2021, 07:25:54) 
[Clang 13.0.0 (clang-1300.0.29.30)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from myapp.models import Task
>>> 

DBのレコードが空なので、以下を行っても空データのようなものが返ってきた。

>>> tasks = Task.objects.all()
>>> print(tasks)
<QuerySet []>

テーブルの構造を確認したい。以下のようにして確認できた。idは主キーである。自動的に増加するBigAutoFieldというデータ型を持つ。

>>> for field in Task._meta.fields:
...     print(field.name, field.get_internal_type())
... 
id BigAutoField
title CharField
description TextField
completed BooleanField

【簡単なAPIを実装】シリアライザーの作成

serializer.pyを新規作成して、必要事項を記入していく。

シリアライザーとは、モデルのデータをJSONに変換できるもの。

シリアライザーをインポートする。

from rest_framework import serializers

同階層にあるmodelsモジュールからTaskクラスをインポートする。

serializers.ModelSerializerを継承することでDjangoのモデルをJSONで扱えるようにする。

class TaskSerializer(serializers.ModelSerializer):

Metaクラスを使ってシリアライザーの設定を行う。Taskモデルの全てのデータを対象にした。

  class Meta:
    model = Task
    fields = '__all__'

serializers.pyの全体はこちら。

from rest_framework import serializers
from .models import Task

class TaskSerializer(serializers.ModelSerializer):
  class Meta:
    model = Task
    fields = '__all__'

【APIのエンドポイントを作成】ビューの作成

APIのエンドポイントとは、フロントエンドや外部システムがDjangoのバックエンドとやり取りするためのURLのこと。

まずは、ビューを作成する。

viewsets、Task、TaskSerializerの3つのクラスをインポートする。

from rest_framework import viewsets
from .models import Task
from .serializers import TaskSerializer

次にviewsets.ModelViewSetを継承したTaskViewsetクラスを作成する。

class TaskViewSet(viewsets.ModelViewSet):

ModelViewSetを使用すると、数行のコードでCRUDを実装できる。

CRUDとは、Create(作成)、Read(取得)、Update(更新)、Delete(削除)のこと。

まずはTaskのレコードを全件取得。

  queryset = Task.objects.all()

そして使用するシリアライザーを指定する。これは先ほど作成したもの。

  serializer_class = TaskSerializer

views.pyの全体はこちら。

from django.shortcuts import render

# Create your views here.
from rest_framework import viewsets
from .models import Task
from .serializers import TaskSerializer

class TaskViewSet(viewsets.ModelViewSet):
  queryset = Task.objects.all()
  serializer_class = TaskSerializer

【APIのエンドポイントを作成】ルーティングの作成

続いてルーティングを追加する。

urls.pyに書いていく。

django.urlsからincludeも読み込む。これはurls.pyでurlを取り込むために使用する。

from django.urls import path, include

ルーティングを管理するために、DefaultRouterを読み込む。

from rest_framework.routers import DefaultRouter

ここで重大なミスに気がついた。

django_todoの中にあるurls.pyを編集してしまっていたが、本来編集すべきは、myappに新規作成したurls.pyであった。

そのためこちらは元に戻した。

myapp直下にurls.pyを作成した。

path関数とinclude関数を読み込むために以下の記述を行う。

from django.urls import path, include

先ほどもお話しした通り、ルーティングを管理するために、DefaultRouterを読み込む。

from rest_framework.routers import DefaultRouter

views.pyから、先ほど定義したTaskViewSetクラスを読み込む。

from .views import TaskViewSet

DefaultRouterのインスタンスを生成する。

router = DefaultRouter()

次に、tasksというURLパターンにTaskViewSetを登録する。rはエスケープシーケンスを無視して、そのままの文字列として扱うために使われる。ここでは不要だが、router.registerでは、rを使うのが一般的だそうだ。URLパターンに\がある場合にエスケープの影響を受けないようにするため。

router.register(r'tasks', TaskViewSet)

最後に、/api/tasks/といったURLパスを生成するために以下の記述を行う。

urlpatterns = [
  path('api/', include(router.urls))
]

動作確認

開発サーバーを起動する。

(venv) Mac:django_todo shibatahiroshitaka$ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
March 01, 2025 - 14:25:49
Django version 4.2.19, using settings 'django_todo.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

commandを押しながらURLをクリックして、djangoのスタートページを開いた。

先ほど作成したapiが動作するか確認するために、以下のURLにアクセスした。

http://127.0.0.1:8000/api/tasks/

エラーが発生した。

これは、myapp/urls.pyがdjango_todo/urls.pyに登録されていないことが原因。

以下の太字部分を追記する。

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

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

今度は別のエラーが発生。api/api/となってしまっている。

これは、myapp/urls.pyのurlpatternsにapiがあることが原因。

これを削除した。

urlpatterns = [
  path('', include(router.urls))
]

今度は別のエラーが発生した。これだからプログラミングは面白い。

このエラーはDjango REST Framework(DRF)のブラウザAPIビューが有効になっているが、テンプレートが正しく設定されていないときに発生する。

設定ファイルにDEFAULT_RENDER_CLASSESを追加して、JSONのみを返すようにする。

そしたら以下の画面になった。どうやらサーバーの再起動が必要のようだ。

サーバーを再起動する。

(venv) Mac:django_todo shibatahiroshitaka$ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
March 01, 2025 - 15:16:53
Django version 4.2.19, using settings 'django_todo.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

状況は何も変わっていなかった。この壁を超えていこう。

myapp > urls.pyのurlpatternsを以下のように変更したが、状況に変化はなかった。

urlpatterns = router.urls

設定ファイルのINSTALLED_APPSにrest_frameworkを追加したら、

無事、画面が表示された。

データを作成する。画面上のフォームを使用することもできるみたいだが、以下のコマンドを使用する。開発サーバーを起動した状態で、新しいターミナルを開いてコマンドを入力した。

(venv) Mac:django_todo shibatahiroshitaka$ curl -X POST -H "Content-Type: application/json" -d '{"title": "Django REST API作成", "description": "最初のタスク", "completed": false}' http://127.0.0.1:8000/api/tasks/
{"id":1,"title":"Django REST API作成","description":"最初のタスク","completed":false}(venv) Mac:django_todo shibatahiroshitaka$

タスクが登録されて、画面にも表示された。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

投稿ID : 28777