このページでは、Python の Django を用いたウェブアプリの開発例として「じゃんけんウェブアプリ」の作り方の紹介をしていきたいと思います。
今回開発するアプリでプレイできるじゃんけんは、あくまでも一人用のゲームとなります(コンピューターと対戦)。
そのため、開発するウェブアプリは単純なものになります。ですが、それでもアプリを開発するためには下記のようなことを実現する必要があり、特に Django 初心者の方であれば学べることや復習できることも多いのではないかと思います。
- ユーザー管理モデルのカスタマイズ
- ユーザー登録とログイン
- 静的ファイルの利用
- 画像のアップロード
- じゃんけんのプレイ
本サイトでは、特に上記の上側の4項目についての個別の解説ページを用意しておりますので、じゃんけんウェブアプリの開発手順を紹介する際には、これらの解説ページへのリンクを参照しながら説明をさせていただきたいと思います。
このページでは、特にじゃんけんウェブアプリを開発するという点に焦点を当てて解説していきます。
Contents
開発する「じゃんけんウェブアプリ」の紹介
最初に、今回開発する「じゃんけんウェブアプリ」がどういったアプリであるかについて説明しておきます。
まず、今回開発する「じゃんけんウェブアプリ」ではログイン機能を用意し、ログインしているユーザーのみがじゃんけんをプレイ可能な作りとしていきたいと思います。
そのため、じゃんけんをプレイするためには事前にユーザーを登録し、さらにそのユーザーでアプリにログインする必要があります。
また、ユーザー登録時にはユーザーのアバターを登録するために画像のアップロードを行う必要があるようにしています。

アプリにログインを行うと下図のようなホームページに遷移し、ここではログイン中のユーザーの情報、具体的にはログイン中のユーザーの名前やアバター画像、さらにはじゃんけんの成績や今まで出した手(グー・チョキ・パー)の回数の統計が表示されるようになっています。

さらに、ページ上部のナビゲーションバーの じゃんけん リンクをクリックすれば下図のようなページに遷移し、ここでじゃんけんをプレイすることができるようになっています。

ユーザーが出したい手を選択し、その手に対応するボタンをクリックすれば、じゃんけんの結果のページに遷移して結果が表示されることになります(相手が出す手はランダムに決定されます)。

また、じゃんけんをプレイする度にユーザーのじゃんけんの成績や統計も更新されるようになっています。
そのため、じゃんけんプレイする度に、ホームページで表示されるじゃんけんの成績や統計もどんどん更新されていくことになります(ホームページには、ナビゲーションバーの ホーム リンクをクリックすることで遷移することができます)。

また、ランキング リンクをクリックすれば、じゃんけんウェブアプリを利用しているユーザーのランキング表が表示されます(じゃんけんの勝率に基づいたランキング)

さらに、このランキングで表示されるユーザー名にはリンクが貼られており、これをクリックすることで、そのユーザーの情報を確認することができるようにもなっています。

あとは、ナビゲーションバーの ログアウト リンクをクリックすることでログアウトすることも可能となっています。
このように、ゲームをプレイできるだけでなく、ログイン機能を搭載することで、他のユーザーの情報や全ユーザーのランキングを表示する機能も備えたウェブアプリを実現していきます。こういった各ユーザーの情報表示やランキング表示に関しては、様々なゲームのウェブアプリに応用できるテクニックになると思います。
ですので、今回は「じゃんけん」という簡単かつポピュラーなゲームを用いたウェブアプリになりますが、今回開発するウェブアプリを応用することで、もっと他のゲームがプレイ可能なウェブアプリを実現したり、あなた自身が考案したゲームをウェブアプリとして開発したりしていくこともできると思います。
じゃんけんウェブアプリの開発手順
開発していくアプリのイメージは掴めたでしょうか?
続いては、じゃんけんウェブアプリの開発手順を紹介していきたいと思います。
スポンサーリンク
事前準備
まず、じゃんけんウェブアプリでは画像を扱うため、画像を扱うためのモジュールが必要になります。具体的には、PIL(Pillow)が必要になります。
そのため、じゃんけんウェブアプリを開発していく前に PIL をインストールしておく必要があります。
PIL をまだインストールしていない方は、下記コマンドによって PIL のインストールを行なっておいてください。
% python -m pip install Pillow
プロジェクト・アプリの作成
では、じゃんけんウェブアプリの開発手順について説明していきます。
まずは、他のウェブアプリ開発時と同様の手順でプロジェクトとアプリを作成していきます。
今回はプロジェクト名を JankenWebappli とし、アプリ名を janken としてアプリを開発していきたいと思います。
最初に、ターミナル等で適当なフォルダに移動し、下記コマンドにより startproject を実行してプロジェクトの作成を行いましょう。
% django-admin startproject JankenWebappli
これにより、今いるフォルダに JankenWebappli というフォルダが作成されますので、そのフォルダに移動し、さらに startapp を実行してアプリの作成を行います。
% cd JankenWebappli % python manage.py startapp janken
これにより、今いるフォルダにアプリ用のフォルダとして janken が作成され、その中に models.py や views.py 等が生成されることになります。このフォルダ内のファイルがアプリを構成することになります。
ここからは、パスを現在いるフォルダ(JankenWebappli フォルダ)からの相対パスで表記していきます
続いて、作成したアプリをプロジェクトに登録します。このアプリの登録は、JankenWebappli/settings.py の INSTALLED_APPS を下記のように変更することで行うことができます。
INSTALLED_APPS = [
    'janken', # 追加
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]静的ファイルを扱うための準備
続いて静的ファイルを扱うための準備をしていきます。
静的ファイルとは、ユーザーからのリクエストに関わらず常に同じ内容になるファイルのことで、今回は静的ファイルとして「画像ファイル(じゃんけんの手を示す画像)」と「CSS ファイル」の2種類を扱っていきます。
ユーザーからのリクエストに応じて内容が変化するファイルのことは動的ファイルと呼び、これに関しての準備は 動的ファイルを扱うための準備 で解説していきます。
画像ファイルの用意
じゃんけんの手を示す画像としては いらすとや の画像を利用させていただくことにしたいと思います。
下記 URL をウェブブラウザで開けばグー・チョキ・パーの画像が表示されますので、右クリック等で画像ファイルを PC に保存してください。
https://www.irasutoya.com/2013/07/blog-post_5608.html
おそらく、保存を行った各画像ファイルの名前は下記のようになると思います。もし異なる名前で保存された場合はファイル名を下記のように変更してください。
- グーの画像:janken_gu.png
- チョキの画像:janken_choki.png
- パーの画像:janken_pa.png
画像ファイルの検索先フォルダへの設置
続いて、保存した画像ファイルを「静的ファイルの検索先フォルダ」以下に設置します。Django では、この「静的ファイルの検索先フォルダ」以下に設置したファイルは静的ファイルとして扱われることになります。
この「静的ファイルの検索先フォルダ」とは、具体的には下記の位置のフォルダとなります。
プロジェクト名/アプリ名/static/アプリ名/
今回の場合、プロジェクト名を JankenWebappli、アプリ名を janken としているため、下記のフォルダが「静的ファイルの検索先フォルダ」となります。
JanakenWebappli/janken/static/janken/
このフォルダに画像ファイルを設置するため、まずは janken フォルダの下に static という名前のフォルダを作成してください。さらに、その static フォルダの下に janken フォルダを作成してください。
また、今回は静的ファイルとして画像だけでなく CSS ファイルも扱います。ファイルの種類ごとにフォルダを用意した方がファイルの管理がしやすいため、最後に作成した janken の下に画像ファイル格納用のフォルダとして img フォルダを作成してください。
そして、その img フォルダの中に、先ほど保存した3つの「じゃんけんの手を示す画像」を設置します。
以上により、JankenWebappli 以下のフォルダ・ファイル構成は下の図のようになると思います(他にもフォルダやファイル等が存在するはずですが、図からは省略させていただいています)。

CSS ファイルの用意
続いて、もう1種類の静的ファイルである CSS ファイルの用意と設置を行なっていきます。
CSS ファイルとしては下記のようなものを利用したいと思います。これをコピペして janken.css として保存してください。
.image-hand {
    width: 200px;
    height: auto;
}
.image-ranking, .image-profile {
    width: 50px;
    height: auto;
}
.container {
    margin : 0px;
}
.content {
    color : #696969;
    margin : 20px;
}
.header {
    color : #696969;
    margin : 20px;
}
.header-text {
    font-size : 32px;
}
.username-text {
    color : #000000;
}
a {
    color : #1e90ff;
    text-decoration: none;
}
a:hover {
    color : #ff7f50;
}
.nav-item {
    margin-left : 20px;
    margin-right : 20px;
    display: inline-block;
    list-style: none;
    font-size : 20px;
}
.nav-bar {
    background-color : #e6e6fa;
    overflow: hidden;
    margin : 0px;
}
.result-win {
    font-size: 30px;
    color: #ff7f50;
}
.result-lose {
    font-size: 30px;
    color: #4169e1;
}
.result-draw {
    font-size: 30px;
    color: #696969;
}
table {
    border-collapse : collapse;
    margin : 20px;
}
.side {
    display : flex;
    flex-wrap : wrap;
}
.table-list td, .table-list th {
    text-align : center;
    border : solid 1px;
    padding : 5px;
}
.table-janken td, .table-janken th {
    text-align : center;
    padding : 5px;
}
.table-account .account-button {
    border : none;
}
.table-account td, .table-account th {
    padding : 5px;
    border : solid 1px #DDDDDD;
}CSS ファイルの検索先フォルダへの設置
さらに、先ほど作成した janken/static/janken フォルダの下に css フォルダを作成し、その css フォルダの中に janken.css を設置してください。
これにより、JankenWebappli 以下のフォルダ・ファイル構成は下の図のようになると思います。

スポンサーリンク
静的ファイルの利用例
このように静的ファイルを設置しておけば、テンプレートから静的ファイルを読み込んだり表示したりすることができるようになります。
例えば先ほど設置した janken_gu.png の画像の表示は、テンプレートに下記のような img タグを追加することで実現することができます。
<img src="{% static 'janken/img/janken_gu.png' %}">表示する画像を指定しているのが {% static '静的ファイルの相対パス' %} の部分で、静的ファイルの相対パス には表示したい画像のファイルパスを「static フォルダからの相対パス」として指定する必要があります。
実際に静的ファイルを利用するテンプレートの例は後述の テンプレートの作成 で紹介します。
また、ここまで解説してきたような静的ファイルの扱い方の詳細については下記ページで解説していますので、詳細は下記ページをご参照いただければと思います。
 【Django入門17】静的ファイルの扱い方(画像・JS・CSS)
  【Django入門17】静的ファイルの扱い方(画像・JS・CSS)  
動的ファイルを扱うための準備
次に、動的ファイルを扱うための準備をしていきます。
今回開発する「じゃんけんウェブアプリ」においてはユーザーの「アバター画像」が動的ファイルに当てはまります。このアバター画像はユーザー毎に画像をアップロードして設定するものであり、ログイン中のユーザーに応じて表示する画像を変更したり、ユーザーが画像をアップロードできるようにフォームを準備したりしていく必要があります。
これらはモデル・フォーム・ビュー・テンプレート等で実現していくものであり、後述で具体的な例を示していきます。
ここでは、画像アップロード先となるフォルダの設定、およびアップロードされた画像へのリクエスト URL の設定を行なっていきます。
これらの設定を行うために、まずは JankenWebappli/settings.py に下記を追記します(追記はファイルの末尾に行うので問題ありません)。
import os
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
MEDIA_URL = 'media/'上記における MEDIA_ROOT が画像のアップロード先となるフォルダの設定を行うための変数になっています。詳しい説明は省略しますが、上記の2行目が実行されることで画像のアップロード先が janken フォルダと同じ階層の media フォルダに設定されます。

さらに、JankenWebappli/urls. py を下記のように変更しましょう。
from django.contrib import admin
from django.urls import path
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
    path('admin/', admin.site.urls),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)この JankenWebappli/urls. py の変更により、/MEDIA_URL から始まる URL へのリクエストが行われた際に、MEDIA_ROOT 以下のフォルダから該当するファイルが取得され、それがレスポンスとしてリクエスト元に返却されるよう Django の開発用ウェブサーバーが動作するようになります。
例えば JankenWebappli/settings.py に上記のように設定しておけば、リクエスト先の URL が /media/img/avator.png の場合、janken フォルダと同じ階層の media フォルダから img/avator.png が取得され、そのファイルがレスポンスとしてリクエスト元に返却されることになります(そして返却された画像がウェブブラウザに表示される)。

また、この media フォルダは MEDIA_ROOT の設定によって画像のアップロード先としても設定されているため、画像のアップロード先からファイルが取得されることになります。そのため、アップロードされた画像をページに表示するような際には上記のような設定を追記しておくと便利です。
特に画像のアップロードに焦点を当てた解説は下記ページで詳細に行なっていますので、もっと詳しく知りたい方は下記ページをご参照いただければと思います。
 【Django】画像をアップロードする
  【Django】画像をアップロードする  
モデルの作成
続いてモデルを作成していきます。
今回開発するウェブアプリでは、じゃんけんの成績や統計データを表示できるよう、ユーザー毎に「じゃんけんの成績・統計」の情報を管理していく必要があります。
また、アバター画像の表示もできるよう、ユーザー毎に「アバター画像」も管理していく必要があります。
さらに、今回はログイン中のユーザーのみがじゃんけんをプレイできるようにするため、ユーザーの「ログイン」を実現していく必要があります。
モデルの定義
このような、「じゃんけんの成績・統計」および「アバター画像」の管理を行い、さらにログインを実現可能なモデルは、下記のように models.py を定義することで実現することができます。
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
    play_num = models.IntegerField(default=0)
    win_num = models.IntegerField(default=0)
    lose_num = models.IntegerField(default=0)
    draw_num = models.IntegerField(default=0)
    win_rate = models.FloatField(default=0.0)
    stone_num = models.IntegerField(default=0)
    scissors_num = models.IntegerField(default=0)
    paper_num = models.IntegerField(default=0)
    image = models.ImageField(upload_to='img/')
    def play(self, player_hand, com_hand):
        if player_hand == com_hand:
            result = 'draw'
        elif player_hand == 'stone' and com_hand == 'scissors' or player_hand == 'scissors' and com_hand == 'paper' or player_hand == 'paper' and com_hand == 'stone':
            result = 'win'
        else:
            result='lose'
        if result == 'draw':
            self.draw_num += 1
        elif result == 'win':
            self.win_num += 1
        elif result == 'lose':
            self.lose_num += 1
        
        if player_hand == 'stone':
            self.stone_num += 1
        elif player_hand == 'scissors':
            self.scissors_num += 1
        else:
            self.paper_num += 1
        self.play_num = self.win_num + self.lose_num + self.draw_num
        self.win_rate = self.win_num / self.play_num * 100
        return resultカスタムユーザーの定義
ウェブアプリにおいてモデルは重要な要素となるため、上記モデルについても少しポイントを解説しておきます。
1つ目のポイントは CustomUser を AbstractUser というモデルを継承して定義している点になります。このモデルの継承を行うだけで、User と同等のフィールドやメソッドを持つモデルを定義することができます。
下記ページでも解説していますが、User を利用すればログイン機能も簡単に実現することができます。なので、User と同等の CustomUser においてもログイン機能を実現可能ということになります。
 【Django】ログイン機能の実現方法(関数ベースビュー編)
  【Django】ログイン機能の実現方法(関数ベースビュー編)  
  【Django】ログイン機能の実現方法(クラスベースビュー編)
  【Django】ログイン機能の実現方法(クラスベースビュー編)  
また、今回は AbstractUser の継承によって CustomUser を定義していますが、リレーションを利用して User を拡張することでも同様のこと(ログイン機能・じゃんけんの成績と統計の管理・アバター画像の管理)を実現することが可能です。このリレーションを利用した User の拡張については下記ページで解説していますので、興味のある方は読んでみてください。
 【Python/Django】OneToOneFieldを利用してUserモデルを拡張する
  【Python/Django】OneToOneFieldを利用してUserモデルを拡張する  
じゃんけんの成績と統計の管理
2つ目のポイントが CustomUser での「じゃんけんの成績・統計」の管理になります。
CustomUser に持たせている play_num から paper_num のフィールドが「じゃんけんの成績・統計」を管理するためのフィールドであり、これらをじゃんけんのプレイ時に更新してやることで、ユーザーの「じゃんけんの成績・統計」を管理していくことができるようにしています。
そして、この更新を行うのが play メソッドになります。引数 player_hand にユーザーの出した手('stone' or 'scissors' or 'paper')を、引数 com_hand に対戦相手の出した手を指定して実行することで、それらの引数に応じてじゃんけんの結果を判断し、それらに基づいて各フィールドが更新されることになります。
例えば player_hand が 'paper' で com_hand が 'stone' の場合、パー対グーでユーザーの勝ちなので win_num が +1 されます。また、ユーザーはパーを出した回数が1増えるので paper_num も +1 されます。さらに、じゃんけんをプレイしたことでプレイ回数と勝率も変化するため、play_num と win_rate の再計算も行なっています。
このように play メソッドが動作するため、じゃんけんプレイをするたびに CustomUser オブジェクトに play メソッドを実行させるようにすれば、適切に「じゃんけんの成績・統計」を管理することができることになります。
画像の管理
3つ目のポイントが CustomUser での「アバター画像」の管理になります。
CustomUser に持たせている image がアバター画像を管理するためのフィールドになります。
さらに、image は ImageField オブジェクトであり、この ImageField は画像のアップロードやアップロードされた画像の管理を行うためのクラスになっています。このフィールドを持たせておくことで、画像のアップロードをユーザーから受け付けるためのフォームを簡単に実現することができます。

また、ImageField で upload_to 引数を指定していますが、この upload_to はアップロードされた画像の保存先を指定する引数となります。
より具体的には、動的ファイルを扱うための準備 で、アップロードされたファイルは MEDIA_ROOT 以下に保存されると説明しましたが、この upload_to 引数により、MEDIA_ROOT 以下のどのフォルダにアップロードされた画像を保存するのかを指定することができます。
今回は upload_to=img を指定しており、さらに MEDIA_ROOT にはプロジェクトのトップフォルダの下の media を指定しているため、下の図のオレンジ部分のフォルダにアップロードされた画像が保存されることになります。

この辺りの説明は 動的ファイルを扱うための準備 でも紹介した下記ページでも解説を行なっておりますので、より詳しく知りたい方は下記ページをご参照ください。
 【Django】画像をアップロードする
  【Django】画像をアップロードする  
スポンサーリンク
認証に利用するモデルの設定
続いて、アプリでの認証時に利用するモデルの設定を行います。
デフォルトでは、認証時に利用するモデルは User に設定されています。
今回は、先ほど作成したCustomUser を認証に利用し、このモデルを用いてログイン等を実現していくことになるため、デフォルトからの設定変更が必要になります。
この設定変更は、JankenWebappli/settings.py に下記を追記することで実現することができます(ファイルの末尾に追記するので良いです)。
AUTH_USER_MODEL = 'janken.CustomUser'上記における janken の部分はアプリ名になります(startapp 実行時に指定するアプリ名)。また、CustomUser の部分は認証時等に利用するモデルのモデル名となります。
上記のように設定を行うことで、ログイン等で認証が行われる際に、ユーザー管理モデルとして CustomUser が利用されるようになります。
さらに AUTH_USER_MODEL に指定したモデルは get_user_model 関数から取得することも可能になります(実際の取得時の処理は後述で紹介していきます)。
マイグレーションの実行
モデルも作成済みですので、次はマイグレーションを実行していきます。
このマイグレーションは、いつも通り下記の2つのコマンドにより実行することができます。
% python manage.py makemigrations % python manage.py migrate
フォームの作成
次はフォームを作成していきます。
今回は、ユーザー登録用のフォームとログイン用のフォームを作成していきます。
フォームの定義
結論としては、janken/forms.py を下記のように新規作成することで、ユーザー登録用のフォームとログイン用のフォームを作成します。
from django import forms
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth import get_user_model
User = get_user_model()
class SignupUserForm(UserCreationForm):
    class Meta:
        model = User
        fields = ['username', 'image']
class LoginForm(AuthenticationForm):
    passフォームの継承
上記 forms.py における SignupUserForm がユーザー登録用のフォームで、LoginForm がログイン用のフォームとなります。
ユーザー登録用のフォームに関しては UserCreationForm、ログイン用のフォームに関しては AuthenticationForm を継承することで簡単に作成することができます。
上記の実装を見ても分かる通り、AuthenticationForm はそのまま使用しても、ユーザー名とパスワードの入力受付を行うログイン用のフォームとして利用することができます。今回は、後から変更がしやすいように AuthenticationForm を継承して別途 LoginForm を作成するようにして居ます。
また、UserCreationForm は ModelForm を継承したフォームになっており、model に指定したモデルの持つフィールドの入力受付を簡単に実現することができます。どのフィールドの入力受付を行うかは fields によって指定を行います。
具体的には、SignupUserForm では model に CustomUser を指定し、fields には ['username', 'image'] を指定しているため、テンプレートから SignupUserForm のオブジェクトの表示を行えば、CustomUser のフィールドである username と image 及びパスワードと確認用パスワードの入力受付を行うフォームが表示されることになります(UserCreationForm の場合、fields に指定しなくてもパスワード及び確認用パスワードの入力受付が自動的に行われるようになります)。

また、CustomUser における image フィールドは ImageField であるため、image の入力受付ではユーザーからのアップロード画像の選択を受け付けるものとなります。
より具体的には、フォーム表示時に下の図のような ファイルを選択 ボタンが表示され、このボタンをクリックすることでアップロードする画像を選択できるようになります。

スポンサーリンク
URL の設定
ここからビューやテンプレートを作成していくことになるのですが、まずは URL の設定を行なっておきたいと思います。
URL とビューの関数の関連付け
まず、JankenWebappli/urls.py を下記のように変更し、ルートパスが janken から始まる場合に janken/urls.py を読み込むようにします。
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
    path('admin/', admin.site.urls),
    path('janken/', include('janken.urls'))
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)続いて janken/urls.py を下記のように新規作成します。
from . import views
from django.urls import path
urlpatterns = [
    path('signup/', views.signup_view, name='signup'),
    path('login/', views.login_view, name='login'),
    path('logout/', views.logout_view, name='logout'),
    path('janken/', views.janken_view, name='janken'),
    path('', views.index_view, name='index'),
    path('user/<int:id>/', views.user_view, name='user'),
    path('ranking/', views.ranking_view, name='ranking'),
]これらの設定により、下記の URL を指定した際に、その URL の右側に記載したビューの関数が実行されるようになります。
- http://localhost:8000/janken/signup/:signup_view
- http://localhost:8000/janken/login/:login_view
- http://localhost:8000/janken/logout/:logout_view
- http://localhost:8000/janken/janken/:janken_view
- http://localhost:8000/janken/:index_view
- http://localhost:8000/janken/user/<int:id>/:user_view
- http://localhost:8000/janken/ranking/:ranking_view
ただ、まだビューを作成していないため上記で挙げた関数は存在しません。なので、現状ではアプリを実行しようとしてもエラーが発生することになるので注意してください。ビューを作成してやれば、アプリが正常に動作するようになります。
LOGIN_URL の設定
もう1つ URL 関連の設定として、LOGIN_URL の設定が必要になります。
この LOGIN_URL の設定は、JankenWebappli/settings.py に下記を追記することで行います(追記場所はファイルの最後で良いです)。
LOGIN_URL = '/janken/login/'LOGIN_URL はログインページの URL を設定するための変数になります。今回開発するウェブアプリにおいては、urls.py でログインページの URL をルートパス指定で /janken/login/ としているため、この URL を LOGIN_URL に設定しています。
ビューの関数に @login_required デコレータの指定を行うことで、特定のページに対する未ログインのユーザーからのアクセスを禁止し、禁止する代わりに LOGIN_URL に指定された URL へのリダイレクトを行うことが可能になります。
ですので、LOGIN_URL にログインページの URL を指定しておけば、未ログインユーザーがアクセスしてきた際にログインページへリダイレクトすることができるようになります。
テンプレートの作成
次は、テンプレートを作成していきます。
開発する「じゃんけんウェブアプリ」の紹介 で紹介したページを実現していくためには、下記の7つのテンプレートが必要になります。
- signup.html:ユーザー登録ページ表示用
- login.html:ログインページ表示用
- janken.html:じゃんけんのプレイ用
- result.html:じゃんけんの結果表示用
- index.html:ログイン中ユーザーの情報表示用
- user.html:他のユーザーの情報表示用
- ranking.html:ランキング表示用
上記の7つのテンプレートにおいて、下側の5つに関しては base.html というテンプレートを継承する形で作成していきます。そのため、その継承元となる base.html の作成も必要になります(つまり合計8つのテンプレートを作成することになります)。
また、各種テンプレートのタグでは class を指定しており、これにより 静的ファイルを扱うための準備 で用意した janken.css のスタイルを適用するようにしています。ですので、各 class の指定によってどのようにスタイルが変化するのかについては、janken.css を別途参照していただければと思います。
フォルダの準備
まずは前述したテンプレートを作成していく前に、テンプレート設置用のフォルダを作成しておきましょう。
下記の2つの手順で、テンプレート設置用のフォルダである janken/templates/janken を作成します。
- jankenフォルダの下に- templatesフォルダを作成する
- 作成した templatesフォルダの下にjankenフォルダを作成する
以降で紹介するテンプレート(.htmlファイル)は、上記の 2. で作成した janken フォルダ内に設置していってください。
signup.html
では、続いてテンプレートファイルを作成していきます。
signup.html はユーザー登録ページ表示用のテンプレートであり、より具体的には、ユーザー登録用のフォームを表示し、そのフォームへのユーザーからの入力受付を行い、さらに入力されたデータを送信するためのテンプレートになります。
signup.html の実装例は下記のようになります。
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>ユーザー登録</title>
    <link rel="stylesheet" href="{% static 'janken/css/janken.css' %}" >
</head>
<body class="container">
    <div class="content">
        <h1 class="header-text">ユーザー登録</h1>
        <form action="{% url 'signup' %}" method="post" enctype="multipart/form-data">
            {% csrf_token %}
            <table class="table-account">
                {{ form.as_table }}
                <tr><th class="account-button"><td class="account-button">
                    <input type="submit" value="登録">
                </td></th></tr>
            </table>
        </form>
    </div>
</body>このテンプレートはビューから form として SignupUserForm のオブジェクトを受け取ることを前提とした作りになっているので注意してください。
いくつか補足しておくと、まず signup.html では link タグでスタイルシートの読み込みを行なっています。読み込んでいるのは、静的ファイルを扱うための準備 で用意した janken.css になります。
また、form タグの内側で {{ form.as_table }} によりビューから受け取ったフォームオブジェクト form の表示を行なっています。なので、ビューから SignupUserForm を form として受け取れば、ユーザー登録用のフォームが表示されることになります。
さらに、form タグの内側で type="submit" を指定した input タグを設置しているため、ボタンがページに表示され、さらにボタンが押された時にはフォームに入力された情報が送信されるようになります。
また、今回はユーザー登録時にアバター画像がアップロードされるため、このアップロードを実現するために form タグには enctype="multipart/form-data" を指定しておく必要があるのでご注意ください。
login.html
login.html はログインページ表示用のテンプレートです。
表示するフォームは SignupUserForm ではなく LoginForm であるという違いはあるものの、テンプレートの作り方のポイントとしては、先ほど紹介した signup.html とほぼ同じになります。
login.html の実装例は下記のようになります。
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>ログイン</title>
    <link rel="stylesheet" href="{% static 'janken/css/janken.css' %}" >
</head>
<body class="container">
    <div class="content">
        <h1 class="header-text">ログイン</h1>
        <form action="{% url 'login' %}" method="post">
            {% csrf_token %}
            <table class="table-account">
                {{ form.as_table }}
                <tr><th class="account-button"><td class="account-button">
                    <input type="submit" value="ログイン"">
                </td></th></tr>
            </table>
        </form>
        <p><a href="{% url 'signup' %}">ユーザー登録をする</a></p>
    </div>
</body>このテンプレートはビューから form として LoginForm のオブジェクトを受け取ることを前提とした作りとなっています。
base.html
base.html は他のテンプレートファイルから継承されることを前提としたテンプレートになります。
base.html で主に行なっているのはスタイルシートの読み込みとナビゲーションバーの表示であり、この base.html を継承することで、同じスタイルのページの表示とナビゲーションバーの表示を実現していきます。
base.html の実装例は下記のようになります。
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>{% block title %}{% endblock %}</title>
    <link rel="stylesheet" href="{% static 'janken/css/janken.css' %}" >
</head>
<body class="container">
    <nav class="nav-bar">
        <ul>
            <li class="nav-item">
                <a class="nav-link" href="{% url 'index' %}">ホーム</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="{% url 'janken' %}">じゃんけん</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="{% url 'ranking' %}">ランキング</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="{% url 'logout' %}">ログアウト</a>
            </li>
            <li class="nav-item">
                <span><span class="username-text">{{user.username}}</span> でログイン中</span>
            </li>
        </ul>
    </nav>
    <div class="header">{% block header %}{% endblock %}</div>
    <div class="content">{% block content %}{% endblock %}</div>
</body>
</html>この base.html を継承する際には、title ブロック、header ブロック、content ブロックの3つのブロックを定義する必要があります。逆に言えば、この3つのブロック以外の部分は base.html を継承することで自動的に作成されるようになります。

以降で紹介していくテンプレートでは、これら3つのブロックの定義のみを行なっています。base.html を継承しているため、上の図の各種ブロックのところに、ここから紹介していくテンプレートで定義されたブロックが埋め込まれて表示されることになります。
janken.html
janken.html はじゃんけんプレイ用のページを表示するためのテンプレートになります。
より具体的には、じゃんけんの手の画像(グー・チョキ・パー)を表示し、さらに各画像の下にボタンを設置するテンプレートとなります。これにより、じゃんけんで出す手をユーザーから選択できるようにします。
janken.html の実装例は下記のようになります。
{% extends 'janken/base.html' %}
{% load static %}
{% block title %}じゃんけん{% endblock %}
{% block header %}
<h1 class="header-text">じゃんけん</h1>
{% endblock %}
{% block content %}
<form action="{% url 'janken' %}" method="post">
    {% csrf_token %}
    <table class="table-janken">
        <tbody>
            <tr><td><img src="{% static 'janken/img/janken_gu.png' %}" class="image-hand"></td>
                <td><img src="{% static 'janken/img/janken_choki.png' %}" class="image-hand"></td>
                <td><img src="{% static 'janken/img/janken_pa.png' %}" class="image-hand"></td>
            </tr>
    
            <tr>
                <td><button type="submit" name="hand" value="stone">グー</button></td>
                <td><button type="submit" name="hand" value="scissors">チョキ</button></td>
                <td><button type="submit" name="hand" value="paper">パー</button></td>
            </tr>
        </tbody>
    </table>
</form>
{% endblock %}前述の通り、この janken.html では1行目で base.html の継承を行い、さらに下記部分でブロックの定義を行なっています。これらが base.html の各ブロックに埋め込まれてページの表示が行われることになります。
- {% block title %}〜- {% endblock %}:- titleブロックの定義
- {% block header %}〜- {% endblock %}:- headerブロックの定義
- {% block content %}〜- {% endblock %}:- contentブロックの定義
以降のテンプレートファイルは全て、janken.html と同様に base.html の継承と3つのブロックの定義を行う形式のものになります。
result.html
result.html はじゃんけんの結果を表示するためのテンプレートになります。
より具体的には、じゃんけんの結果(勝ち・負け・引き分け)と、ユーザーが選択した手&対戦相手が選択した手の表示を行うテンプレートになります。
result.html の実装例は下記のようになります。
{% extends 'janken/base.html' %}
{% load static %}
{% block title %}じゃんけん{% endblock %}
{% block header %}
<h1 class="header-title">じゃんけんの結果</h1>
{% endblock %}
{% block content %}
<table class="table-janken">
    <thead>
        <tr><th colspan="2">
            {% if result == "win" %}
            <p class="result-win">あなたの勝ちです</p>
            {% elif result == "lose" %}
            <p class="result-lose">あなたの負けです</p>
            {% else %}
            <p class="result-draw">引き分けです</p>
            {% endif %}
        </th></tr>
    </thead>
    <tbody>
        <tr>
            <td>あなたが出した手</td><td>相手が出した手</td>
        </tr>
        <tr>
            <td>
                {% if player_hand == "stone" %}
                <img src="{% static 'janken/img/janken_gu.png' %}" class="image-hand">
                {% elif player_hand == "scissors" %}
                <img src="{% static 'janken/img/janken_choki.png' %}" class="image-hand">
                {% else %}
                <img src="{% static 'janken/img/janken_pa.png' %}" class="image-hand">
                {% endif %}
            </td>
            <td>
                {% if com_hand == "stone" %}
                <img src="{% static 'janken/img/janken_gu.png' %}" class="image-hand">
                {% elif com_hand == "scissors" %}
                <img src="{% static 'janken/img/janken_choki.png' %}" class="image-hand">
                {% else %}
                <img src="{% static 'janken/img/janken_pa.png' %}" class="image-hand">
                {% endif %}
            </td>
        </tr>
        <tr>
            <td colspan="2"><a href="{% url 'janken' %}">もう一度プレイする</a></td>
        </tr>
    </tbody>
</table>
{% endblock %}この result.html は、result として「じゃんけんの結果("win"・"lose"・"draw")」、player_hand として「ユーザーが出した手("stone"・"scissors"・"paper")」、com_hand として「対戦相手が出した手("stone"・"scissors"・"paper")」をビューから受け取ることを前提とした作りになっているので注意してください。
index.html
index.html はログイン中ユーザーの情報を表示するためのテンプレートになります。
ログイン中ユーザーの「名前」や「アバター画像」、さらには「じゃんけんの成績や統計データ」の表示を行います。
これらの情報は全て CustomUser がフィールドとして持っていますので、CustomUser のオブジェクトを受け取り、そのオブジェクトのフィールドにアクセスして情報を表示すれば良いだけになります。
index.html の実装例は下記のようになります。
{% extends 'janken/base.html' %}
{% block title %}あなたの情報{% endblock %}
{% block header %}
<h1 class="header-string">ホームページ</h1>
<p>{{user.username}}さん こんにちは!</p>
{% endblock %}
{% block content %}
<img src="{{user.image.url}}" class="image-profile">
<div class="side">
    <table class="table-list">
        <thead>
            <tr><th colspan="2">{{user.username}} さんの成績</th></tr>
        </thead>
        <tbody>
            <tr><td>プレイ回数</td><td>{{user.play_num}}回</td></tr>
            <tr><td>勝ち</td><td>{{user.win_num}}回</td></tr>
            <tr><td>負け</td><td>{{user.lose_num}}回</td></tr>
            <tr><td>引き分け</td><td>{{user.draw_num}}回</td></tr>
            <tr><td>勝率</td><td>{{user.win_rate|floatformat}}%</td></tr>
        </tbody>
    </table>
    <table class="table-list">
        <thead>
            <tr>
                <th colspan="2">{{user.username}} さんの統計</th>
            </tr>
        </thead>
        <tbody>
            <tr><td>グーの回数</td><td>{{user.stone_num}}回</td></tr>
            <tr><td>チョキの回数</td><td>{{user.scissors_num}}回</td></tr>
            <tr><td>パーの回数</td><td>{{user.paper_num}}回</td></tr>
        </tbody>
    </table>
</div>
{% endblock %}この index.html に関しては、user としてログイン中のユーザーの CustomUser のオブジェクトをビューから受け取ることを前提とした作りになっています。
user.html
user.html はユーザーの情報を表示するためのテンプレートになります。
基本的に表示する内容自体は index.html と同じですが、index.html はログイン中のユーザーの情報を表示することを目的としたテンプレートであるのに対し、user.html では他のユーザーの情報を表示することを目的としたテンプレートなので、若干表示される文言などが異なります。
ですが、CustomUser のオブジェクトをビューから受け取り、そのオブジェクトの情報を表示するという点では index.html と同じになります。
user.html の実装例は下記のようになります。
{% extends 'janken/base.html' %}
{% block title %}ユーザー情報{% endblock %}
{% block header %}
<h1 class="header-string">ユーザー情報</h1>
<p>{{other.username}}さんの情報</p>
{% endblock %}
{% block content %}
<img src="{{other.image.url}}" class="image-profile">
<div class="side">
    <table class="table-list">
        <thead>
            <tr><th colspan="2">{{other.username}} さんの成績</th></tr>
        </thead>
        <tbody>
            <tr><td>プレイ回数</td><td>{{other.play_num}}回</td></tr>
            <tr><td>勝ち</td><td>{{other.win_num}}回</td></tr>
            <tr><td>負け</td><td>{{other.lose_num}}回</td></tr>
            <tr><td>引き分け</td><td>{{other.draw_num}}回</td></tr>
            <tr><td>勝率</td><td>{{other.win_rate|floatformat}}%</td></tr>
        </tbody>
    </table>
    <table class="table-list">
        <thead>
            <tr><th colspan="2">{{other.username}} さんの統計</th></tr>
        </thead>
        <tbody>
            <tr><td>グーの回数</td><td>{{other.stone_num}}回</td></tr>
            <tr><td>チョキの回数</td><td>{{other.scissors_num}}回</td></tr>
            <tr><td>パーの回数</td><td>{{other.paper_num}}回</td></tr>
        </tbody>
    </table>
</div>
{% endblock %}この user.html に関しては、other としてユーザーの CustomUser のオブジェクトをビューから受け取ることを前提とした作りになっています。より具体的には、次に紹介するランキング表示用ページで選択されたユーザーの CustomUser のオブジェクトをビューから受け取ることを想定しています。
ranking.html
ranking.html は「じゃんけんの勝率に基づいたランキング」を表示するためのテンプレートになります。
ランキングでは、勝率の高いユーザーから順に各ユーザーの情報の表示を行います。ユーザーの情報とは、具体的にはユーザー名とアバター画像と勝率とプレイ回数になります。
また、ユーザー名にはリンクを設定し、そのユーザーの情報をユーザー名をクリックすることで表示できるようにしていきます。
CustomUser のオブジェクトを受け取ってユーザーの情報を表示するという意味では index.html や user.html と同様ですが、複数のユーザーの情報を表示する必要があるため、クエリーセットとして CustomUser のオブジェクトを複数受け取る必要があります。
ranking.html の実装例は下記のようになります。
{% extends 'janken/base.html' %}
{% block title %}ランキング{% endblock %}
{% block header %}
<h1 class="header-string">ランキング</h1>
{% endblock %}
{% block content %}
<table class="table-list">
    <thead>
        <tr>
            <th>ユーザー名</th><th>アバター</th></th><th>勝率</th><th>プレイ回数</th>
        </tr>
    </thead>
    <tbody>
    {% for user in users %}
        <tr>
            <td><a href="{% url 'user' user.id %}">{{user.username}}</a></td>
            <td><img src="{{user.image.url}}" class="image-ranking"></td>
            <td>{{user.win_rate|floatformat}}%</td>
            <td>{{user.play_num}}回</td>
        </tr>
    {% empty %}
        <tr><td>ユーザーがいません...</td></tr>
    {% endfor %}
    </tbody>
</table>
{% endblock %}ranking.html では、users として CustomUser のオブジェクトのクエリーセットを受け取ることを前提としています。また、users に含まれる CustomUser のオブジェクトは「勝率(win_rate)」に対して降順に整列されていることを前提としています。
これらを前提とすることで、{% for user in users %} によって users の先頭から順に CustomUser のオブジェクトを取得して情報を表示するようにするだけで、ランキング形式の表示を行うことができるようになっています。
ビューの作成
最後にビューを作成していきます。
janken/views.py に下記の7つの関数を定義することで、じゃんけんウェブアプリを実現するためのビューを作成します。
- signup_view:ユーザー登録
- login_view:ログイン
- logout_view:ログアウト
- janken_view:じゃんけんのプレイ
- index_view:ログイン中ユーザーの情報表示
- user_view:他のユーザーの情報表示
- ranking_view:ランキング表示
上記の関数では、関数名の右側に記載した動作を実現するための処理を行い、さらに render 関数で テンプレートの作成 で作成したテンプレートの表示を行なっています。
テンプレートの作成 でも説明したように、各種テンプレートはビューから受け取ることを前提としているデータ(オブジェクト)がありますので、それを要素に含む辞書データを render 関数の第3引数に指定する必要があるという点が、ビュー作成時の1つのポイントになるかと思います。
views.py の定義
じゃんけんウェブアプリにおける janken/views.py の定義例は下記のようになります。
from django.shortcuts import render, redirect, get_object_or_404
from .forms import SignupUserForm, LoginForm
from django.contrib.auth import login, logout
from django.contrib.auth.decorators import login_required
from django.contrib.auth import get_user_model
import random
User = get_user_model()
def signup_view(request):
    if request.method == 'POST':
        form = SignupUserForm(request.POST, request.FILES)
        if form.is_valid():
            user = form.save()
            login(request, user)
            return redirect(to='/janken/')
    else:
        form = SignupUserForm()
    param = {
        'form': form
    }
    return render(request, 'janken/signup.html', param)
def login_view(request):
    if request.method == 'POST':
        form = LoginForm(request, data=request.POST)
        if form.is_valid():
            user = form.get_user()
            if user:
                login(request, user)
                return redirect(to='/janken/')
    else:
        form = LoginForm()
    param = {
        'form': form,
    }
    return render(request, 'janken/login.html', param)
@login_required
def ranking_view(request):
    users = User.objects.order_by('-win_rate')
    params = {
        'users' : users
    }
    return render(request, 'janken/ranking.html', params)
@login_required
def logout_view(request):
    logout(request)
    return redirect(to='/janken/login/')
@login_required
def index_view(request):
    params = {
        'user' : request.user
    }
    return render(request, 'janken/index.html', params)
    
@login_required
def user_view(request, id=0):
    other = get_object_or_404(User, id=id)
    params = {
        'user' : request.user,
        'other' : other
    }
    return render(request, 'janken/user.html', params)
@login_required
def janken_view(request):
    if request.method == 'POST':
        com_hand = random.choice(('stone', 'scissors', 'paper'))
        player_hand = request.POST.get('hand')
        player = request.user
        result = player.play(player_hand, com_hand)
        player.save()
        params = {
            'user' : request.user,
            'result' : result,
            'player_hand' : player_hand,
            'com_hand' : com_hand
        }
        return render(request, 'janken/result.html', params)
    else:
        params = {
            'user' : request.user
        }
        return render(request, 'janken/janken.html', params)大雑把に言えば、この janken/views.py でやっていることは下記の4つです。
- ユーザーの登録
- ログイン(ログアウト)
- ユーザーの情報表示
- じゃんけんのプレイ(結果表示)
ログイン関連のポイント
特に上側の3つに関しては、下記のログインの実現の解説ページで説明している内容を踏襲しているため、詳細については下記ページを参考にしていただければと思います(@login_required の意味合いなども理解していただけると思います)。
 【Django】ログイン機能の実現方法(関数ベースビュー編)
  【Django】ログイン機能の実現方法(関数ベースビュー編)  
ポイントを挙げるとすれば、今回のじゃんけんウェブアプリにおいてはユーザーのモデルとして Django に標準で用意されている User  ではなく、CustomUser を使用する必要があるという点になると思います。
上記の janken/views.py においては特定の関数で User を利用しているようにも見えますが、この User は get_user_model 関数の返却値になります。
さらに、認証に利用するモデルの設定 で解説した通り、get_user_model 関数は AUTH_USER_MODEL に指定されたモデルを返却する関数ですので、user_view や ranking_view の中で使用している User は Django に標準で用意されている User  ではなく CustomUser であることになります。
また、ranking_view に関してはランキング表示を行う必要があるため、User.objects.order_by('-win_rate') を実行して勝率に対して降順に並べた状態の CustomUser のクエリーセットを取得しています。
じゃんけんプレイのポイント
じゃんけんのプレイ(結果表示)に関しては janken_view で実現しており、request.method == 'POST' が不成立する場合は、単に janken.html の表示のみを行い、これによってボタンによるユーザーの出す手の選択を受け付けます。

ボタンが押されると、request.method == 'POST' が成立する状態で janken_view が実行されることになります(そうなるように janken.html を作成しています)。さらに、ユーザーが出した手がフォームから送信されてくることになります。
なので、対戦相手が出す手をランダムに決めてやれば、ユーザーが出した手と対戦相手が出した手の両方が揃うことになります。ですので、これらの両方の手を引数に指定して、player に play メソッドを実行させて player のじゃんけんの成績と統計データを更新しています(player は request.user であり、ログイン中のユーザーに対応する CustomUser のオブジェクトとなります)。
さらに、更新後のデータをデータベースに反映させるために player に save メソッドを実行させています。
この辺りの処理は janken_view における下記で行っています。
com_hand = random.choice(('stone', 'scissors', 'paper'))
player_hand = request.POST.get('hand')
player = request.user
result = player.play(player_hand, com_hand)
player.save()また、play メソッドの返却値はじゃんけんの結果となりますので、この結果等の必要なデータをセットした辞書を render 関数の第3引数に指定して result.html の表示を行うことで、じゃんけんの結果が表示されるようにしています。

スポンサーリンク
動作確認
ちょっと解説が長くなってしまいましたが、以上によって「じゃんけんウェブアプリ」が完成したことになります。
最後に動作確認を行なっておきましょう!
まず、下記コマンドを実行して Django 開発用ウェブサーバーを起動します。
python manage.py runserver
続いてウェブブラウザで下記 URL を開いてください。
http://localhost:8000/janken/signup/
すると、下の図のようなユーザー登録用のページが表示されますので、ユーザー名とアバター画像として使用する画像のパス(アップロードする画像のパス)、パスワードの入力を行なって 登録 ボタンをクリックしてください。
画像のパスは ファイルを選択 ボタンをクリックし、PC 内の画像を選択することで指定することができます。

登録 ボタンを押せばユーザー登録が行われ、さらにユーザー登録に成功すればログインが行われます(ユーザー名が重複していたり、パスワードが不適切な場合はユーザー登録に失敗します)。
そして、ログイン後には下の図のような「ログイン中ユーザーの情報表示ページ」に遷移します。登録したユーザー名やアップロードした画像が表示されていることが確認できると思います。じゃんけんの成績や統計に関しては、まだじゃんけんをプレイしていないため、全て 0 になっているはずです。

この「ログイン中ユーザーの情報表示ページ」の表示結果より、ユーザー登録や画像のアップロード、さらにアップロードした画像の表示が実現できていることを確認できると思います。
さらに、ページ上部のナビゲーションバーから じゃんけん のリンクをクリックすれば、下の図のような「じゃんけんをプレイするページ」に遷移します。

グー ボタン・チョキ ボタン・パー ボタン のいずれかのボタンをクリックすれば、クリックしたボタンに応じてじゃんけんの結果が判断され、下の図のような「じゃんけんの結果の表示ページ」に遷移することになります。

じゃんけんの結果が表示された後に、今度はページ上部のナビゲーションバーから ホーム のリンクをクリックすれば、ログイン直後にも表示された「ログイン中ユーザーの情報表示ページ」に再び遷移します。ここで、表内のデータを見てみると、先程選択した手(グー・チョキ・パー)と結果に応じて成績と統計が更新されていることが確認できると思います。

このように、今回開発したウェブアプリでは、じゃんけんをプレイするたびに上記の成績や統計が更新されていくようになっています。
続いて、ページ上部のナビゲーションバーから ランキング のリンクをクリックすると「ランキング表示ページ」に遷移します。

が、まだユーザーが一人なので、先ほど登録したユーザーしか表示されないと思います。これだとランキングっぽくないので、もう一人ユーザーを作成してみましょう!
まず、ページ上部のナビゲーションバーから ログアウト のリンクをクリックしてください。そうするとログアウトが行われ、ログインページに遷移するはずです。

続いて、ログインページの下側にある ユーザー登録をする リンクをクリックし、再度ユーザー登録ページを表示してください。そして、先ほどと同様の手順で、先ほどとは異なるユーザー名のユーザーを登録してください。
ユーザー登録に成功すれば、これも先ほどと同様に「ログイン中ユーザーの情報表示ページ」に遷移するはずです。が、登録したばかりのユーザーなので、再びじゃんけんの成績や統計は 0 になっているはずです。これは、先程のユーザーの成績や統計がクリアされたのではなく、異なるユーザー(新規登録したユーザー)でログインしたためです。

続いて、ページ上部のナビゲーションバーから ランキング のリンクをクリックしてください。そうすると、今度は二人分のランキングが表示されているはずです。
このように、登録されているユーザーに応じてランキングに表示されるユーザー数は変化します。同様の手順でユーザー登録を新たに行えば、またランキングに表示されるユーザーが増えることになります。もちろん、じゃんけんをプレイして勝率を上げれば、そのユーザーがランキング上位に表示されることになります。
また、ランキングのユーザー名にはリンクが貼ってありますので、現在ログイン中のユーザーではない方のユーザー名をクリックしてみてください。
そうすると、最初に作成したユーザーのじゃんけんの成績や統計等が表示されることが確認できるはずです。

ここまで出来れば、今回開発しようとしていたじゃんけんウェブアプリが無事に開発できていることが確認できたと言って良いと思います。
まとめ
このページでは、Django を利用した「じゃんけんウェブアプリ」について紹介を行いました!
簡単なウェブアプリではありますが、下記のようなことを実現する必要があり、ウェブアプリを開発していく上で重要なポイントを含んだアプリになっていると思います。
- ユーザー管理モデルのカスタマイズ
- ユーザー登録とログイン
- 静的ファイルの利用
- 画像のアップロード
- じゃんけんのプレイ
特に上側の4つの項目に関しては、どんなウェブアプリにも応用できると思います。Django を利用するのであれば、これらの実現方法を知っておいて損はないので、是非この機会に覚えておいてください!
また、本サイトでは上側の4つの項目に関する解説記事を公開しています。
具体的には、「ユーザー管理モデルのカスタマイズ」については下記の記事で、
 【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractUser編】
  【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractUser編】  
  【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractBaseUser編】
  【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractBaseUser編】  
  【Python/Django】OneToOneFieldを利用してUserモデルを拡張する
  【Python/Django】OneToOneFieldを利用してUserモデルを拡張する  
「ユーザー登録とログイン」については下記の記事で、
 【Django】ログイン機能の実現方法(関数ベースビュー編)
  【Django】ログイン機能の実現方法(関数ベースビュー編)  
  【Django】ログイン機能の実現方法(クラスベースビュー編)
  【Django】ログイン機能の実現方法(クラスベースビュー編)  
「静的ファイルの利用」については下記の記事で、
 【Django入門17】静的ファイルの扱い方(画像・JS・CSS)
  【Django入門17】静的ファイルの扱い方(画像・JS・CSS)  
「画像のアップロード」については下記の記事で詳細な解説を行なっています。
 【Django】画像をアップロードする
  【Django】画像をアップロードする  
興味があれば、これらの記事も是非読んでみてください!


