0
2

More than 1 year has passed since last update.

Django & JavaScriptで非同期通信のアプリを作る

Last updated at Posted at 2023-07-29

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 がインストールされていることを確認します。
pipDjangoのインストールに使うソフトウェアです。

% python -m pip install --upgrade pip

requirementsファイルによってパッケージをインストールする
requirementsファイルは pip install でインストールする依存関係の一覧が記載されているファイルです。

ajax_practice/ フォルダ内に requirements.txt というファイルを作成します。

% touch requirements.txt 

ディレクトリは以下になります。

ajax_practice
├── ajax
│   └── ...
└───requirements.txt

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にいくつか変更を加えます。

settings.py
LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

静的ファイルのパスも追加する必要があります。
STATIC_URLの下に STATIC_ROOTを追加します。

settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

新しいアプリケーションの作成

% python manage.py startapp myapp

上記のコマンドはDjangoプロジェクト内でapp新しいアプリケーションを作成するためのコマンドです。

アプリケーションを作ったら、Djangoにそれを使うように伝える必要があるので、 mysite/settings.py で設定します。

INSTALLED_APPS を見つけて以下のように追加します。

settings.py
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の部分を以下のように編集します。

settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        ...
    },
]

テンプレートの作成

プロジェクトのルートディレクトリにtemplatesというディレクトリを作成します。その中にindex.htmlというファイルを作成します。

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ファイルも用意しておきます。

static/css/style.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に書かせました。

myapp/views.py
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の設定を追加します。

myproject/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('myapp.urls')),
]
myapp/urls.py
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 を用います。

script.js
$(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の冒頭に以下を足します。

settings.py
import os  # 追加

from pathlib import Path

Webサーバーを起動します

% python manage.py runserver

ブラウザでhttp://127.0.0.1:8000/を開きます。

これで、PythonとDjangoを使ってバナーをクリックした際にテキストや画像が非同期で書き換わるWebアプリケーションが完成しました!

2b5d1e6273dec26b7a28f31e43bc6c1e.jpg

0
2
0

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
0
2