PythonとDjangoを使って簡単なWebアプリケーションを作ってみます。
また非同期通信の練習として、以下の要件を満たしたものにします。
・バナーとテキストを用意して、バナーをクリックしたら画面のリロードが走らずにテキストと画像が書き換わるシンプルなプログラム
一般的なノベルゲームをイメージするとわかりやすいです。
最終的なディレクトリ構成は以下になります。
ajax_practice
├── myapp
│ ├── ...
├── db.sqlite3
├── manage.py
├── myproject
│ ├── ...
├── ajax
│ └── ...
├── static
│ └── css
│ └── style.css
│ └── images
│ ├── default_image.jpg
│ └── new_image.jpg
│ └── js
│ └── script.js
├── templates
│ └── index.html
└── requirements.txt
また、今回のシンプルなWebアプリケーションではデータベースを使用しないため、モデルは作成しません。
環境:MacOS
ホームディレクトリに新しい練習用のディレクトリを作成
(別にルートディレクトリでも良いです)
% mkdir ajax_practice
% cd ajax_practice
仮想環境を作成
% python -m venv ajax
ajax
はここでは仮想環境の名前で、任意の名前を指定できます。
仮想環境を有効化する
% source ajax/bin/activate
これで仮想環境が起動できました。
(ajax) ***** $
のようになっていたらOKです。
Djangoをインストールする
その前に、以下のコマンドで最新バージョンの pip
がインストールされていることを確認します。
pip
はDjango
のインストールに使うソフトウェアです。
% python -m pip install --upgrade pip
requirementsファイルによってパッケージをインストールする
requirementsファイルは pip install
でインストールする依存関係の一覧が記載されているファイルです。
ajax_practice/
フォルダ内に requirements.txt
というファイルを作成します。
% touch requirements.txt
ディレクトリは以下になります。
ajax_practice
├── ajax
│ └── ...
└───requirements.txt
requirements.txt
ファイル中に以下のテキストを追加します:
Django~=3.2.10
そして、下記のコマンドを実行してDjangoをインストールします。
$ pip install -r requirements.txt
Djangoプロジェクトの作成
$ django-admin startproject myproject .
myprojectはプロジェクトの名前で、任意の名前を指定できます。
コマンドの最後にピリオド .
を入力しますが、このピリオドは現在の作業ディレクトリに Django をインストールするということを示しています。
django-admin.py
は、必要なディレクトリとファイルを作成するスクリプトです。
設定
myproject/settings.py
にいくつか変更を加えます。
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'
静的ファイルのパスも追加する必要があります。
STATIC_URL
の下に STATIC_ROOT
を追加します。
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
新しいアプリケーションの作成
% python manage.py startapp myapp
上記のコマンドはDjangoプロジェクト内でapp新しいアプリケーションを作成するためのコマンドです。
アプリケーションを作ったら、Djangoにそれを使うように伝える必要があるので、 mysite/settings.py
で設定します。
INSTALLED_APPS
を見つけて以下のように追加します。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'myapp', # 新しいアプリケーションを追加
]
Djangoプロジェクトの設定:
まず、Djangoプロジェクトのsettings.py
ファイルを編集して、テンプレートのディレクトリを設定します。TEMPLATESセクションにあるDIRSの部分を以下のように編集します。
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
...
},
]
テンプレートの作成
プロジェクトのルートディレクトリにtemplates
というディレクトリを作成します。その中にindex.html
というファイルを作成します。
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="/static/css/style.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<p id="text">かつて、遥か彼方の宇宙に、ひとつの美しい星がありました。</p>
<img id="image" src="/static/images/default_image.jpg">
<div class="banner" id="banner"></div>
<script src="/static/js/script.js"></script>
</body>
</html>
同時にcssファイルも用意しておきます。
/* style.css */
.banner {
position: fixed;
bottom: 0;
width: 100%;
height: 20%; /* 画面の下半分を占めるように設定 */
background-color: rgba(0,0,0,0.8); /* 背景色をより濃い黒色に設定 */
cursor: pointer; /* カーソルを指アイコンにする */
}
#text {
position: fixed;
bottom: 0; /* バナーと同じ位置に配置 */
color: white; /* テキストの色を白に設定 */
z-index: 2; /* テキストを前面に表示 */
}
ビューの作成:
myapp/views.py
ファイルに以下のようにビューを作成します。
※ストーリーはChatGPTに書かせました。
from django.http import JsonResponse
from django.shortcuts import render
def change_text(request):
if request.is_ajax():
story = [
"かつて、遥か彼方の宇宙に、ひとつの美しい星がありました。",
"そこには、様々な生命が共存し、平和な時代が続いていました。",
"しかし、ある日、暗黒の力が星を覆い始めました。",
"暗黒の力は、星の生命たちを次々と闇へと変えていきました。",
"星の生命たちが絶望する中、一筋の光が現れました。",
"それは、星の守護神が遣わしたとされる、勇者の光でした。",
"勇者は、星を救うため、一人で暗黒の力に立ち向かう決意をしました。",
"長い戦いが続き、勇者は何度も傷つきながらも、決して諦めませんでした。",
"そしてついに、勇者は暗黒の力を封じ、星に平和を取り戻しました。",
"それ以降、星の生命たちは勇者を祝福し、平和な時代が再び訪れました。"
]
new_image_url = "/static/images/new_image.jpg" if index == 5 else "/static/images/default_image.jpg"
return JsonResponse({'story': story, 'new_image_url': new_image_url})
def index(request):
return render(request, 'index.html')
URLの設定:
myproject/urls.py
ファイルにURLの設定を追加します。
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('myapp.urls')),
]
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('change_text/', views.change_text, name='change_text'),
]
staticディレクトリの作成 & 静的ファイルの配置
静的ファイル (画像ファイルなど) は、プロジェクトのルートディレクトリにあるstatic
ディレクトリに配置します。
まず、Djangoプロジェクトのルートディレクトリにstatic
という名前のディレクトリを作成します。
このディレクトリに default_image.jpg
ファイル(デフォルトの画像)と new_image.jpg
ファイル(変更先の画像)を配置します。
% mkdir static
% mkdir static/images
% touch static/images/default_image.jpg
% touch static/images/new_image.jpg
そして、それぞれの画像ファイルには何でも良いので、画像を用意しておきます。
JavaScriptファイルの作成
さらに static
ディレクトリの下にjs
ディレクトリを作成し、その中にscript.js
ファイルを作成します。
% mkdir static/js
% touch static/js/script.js
非同期でテキストを表示させるために Ajax を用います。
$(document).ready(function() {
let index = 0;
let intervalId = null;
let typingSpeed = 40;
let isTyping = false;
let story = [
{text: "かつて、遥か彼方の宇宙に、ひとつの美しい星がありました。", image: "/static/images/default_image.jpg"},
{text: "そこには、様々な生命が共存し、平和な時代が続いていました。", image: "/static/images/default_image.jpg"},
{text: "しかし、ある日、暗黒の力が星を覆い始めました。", image: "/static/images/dark_image.jpg"},
{text: "暗黒の力は、星の生命たちを次々と闇へと変えていきました。", image: "/static/images/dark_image.jpg"},
{text: "星の生命たちが絶望する中、一筋の光が現れました。", image: "/static/images/light_image.jpg"},
{text: "それは、星の守護神が遣わしたとされる、勇者の光でした。", image: "/static/images/light_image.jpg"},
{text: "勇者は、星を救うため、一人で暗黒の力に立ち向かう決意をしました。", image: "/static/images/hero_image.jpg"},
{text: "長い戦いが続き、勇者は何度も傷つきながらも、決して諦めませんでした。", image: "/static/images/hero_image.jpg"},
{text: "そしてついに、勇者は暗黒の力を封じ、星に平和を取り戻しました。", image: "/static/images/peace_image.jpg"},
{text: "それ以降、星の生命たちは勇者を祝福し、平和な時代が再び訪れました。", image: "/static/images/peace_image.jpg"}
];
function typeWriter(text, callback) {
let i = 0;
isTyping = true;
intervalId = setInterval(function() {
$("#text").append(text.charAt(i));
i++;
if (i === text.length) {
clearInterval(intervalId);
isTyping = false;
typingSpeed = 40;
callback();
}
}, typingSpeed);
}
function loadStory() {
if (index === story.length) {
return;
}
clearInterval(intervalId);
$("#text").empty();
typeWriter(story[index].text, function() {});
$("#image").attr('src', story[index].image);
index++;
}
$("#banner").click(function() {
console.log("Banner clicked!");
if (isTyping) {
console.log("Typing... speeding up.");
clearInterval(intervalId);
typingSpeed /= 4;
typeWriter(story[index - 1].text.slice($("#text").text().length), function() {});
} else {
console.log("Loading next story...");
loadStory();
}
});
loadStory();
});
※このままだとエラーになるので、settings.py
の冒頭に以下を足します。
import os # 追加
from pathlib import Path
Webサーバーを起動します
% python manage.py runserver
ブラウザでhttp://127.0.0.1:8000/
を開きます。
これで、PythonとDjangoを使ってバナーをクリックした際にテキストや画像が非同期で書き換わるWebアプリケーションが完成しました!