注目キーワード
  1. Photoshop
  2. Python
  3. Raspberry Pi
  4. Arduino

Flask入門編02 Pythonを使って簡単なログインWebアプリを作成する

  • 2020年12月7日
  • 2020年12月9日
  • Python

ここでは「Flask入門編」ということで「Python」を使った簡単なWebアプリの制作方法を紹介します。

第2回目ということで「Flask」を使った簡単なログインWebアプリを作成します。

 

まずは実際に作成したものを紹介します。

flask02_test_movie_0

※ 書籍「Flaskで学ぶWebアプリケーションのしくみとつくり方」を参考に改良・改善をしています。

 

この「Flask入門編02」を読むことで

FlaskのGET/POSTの仕組み

Flaskのテンプレート継承

Flaskの動的URLの仕組み

値の一部削除と全削除(.pop()、.clear() )

などを学ぶことができます。

 

したがって

PythonとFlaskを使い、ログイン用Webアプリを作りたい方

Webアプリの開発がどんなものか知りたい方

に役立てて頂ければ幸いです。

 

それでは始めていきましょう!

 

※ Flask入門編では言語「Python」のほかに、「HTML」、「CSS」を使用します。

HTMLとCSSの基礎知識があり、ある程度読めることを前提として進めますのでご理解いただけますと幸いです。

また、今回の記事は前回の「Flask入門編01」の続きとなっております。

Flaskの基礎、HTML・CSS読み込みの知識にまだ不安がある方は、前回の記事をお先にご覧いただくことをオススメします。

Flask入門編01 Pythonを使って簡単なWebアプリを作成する

 

Flaskでフォームの送信をする

まず、ログイン用のWebアプリケーションを作成するには「フォームの送信」の理解が必須になります。

したがって、Flaskでフォームを送信するために使用する「GET/POST」について説明をしていきます。

 

Flaskプログラムの種類とパス

まずはFlaskでWebアプリケーションを作成するためのファイルとパスを下記のように準備します。

📁 C:

➡ 📁 code

➡  📁 frask_test

➡  app.py

➡  📁 templates

➡ index.html

上記のように、Cドライブ直下に「code > frask_test」とファイルを作り、その中に「app.py」のPythonファイルを作成しました。

また、「frask_test > templatesフォルダ」と作成し、このフォルダ内にHTMLファイルを作成します。

(※これは前回の「Flask入門編01」と同じですが、確認です。)

 

作成ができたら、下記の「app.py」と「index.html」の内容をコピペして動作準備をします。

 

app.py

from flask import Flask, render_template, request
app = Flask(__name__)    # Flaskインスタンス作成

f_data = ['サイトポリシー', 'プライバシーポリシー', 'サイトマップ']

@app.route('/',methods=['GET'])
def index():
    return render_template('login.html', \
        title="フォーム送信のテスト", \
        message="メッセージを送信し表示させてみよう!", \
        data = f_data)

@app.route('/',methods=['POST'])
def post():
    form = request.form['form']
    return render_template('login.html', \
        title="フォーム送信のテスト", \
        message= form, \
        data = f_data)

if __name__ == '__main__':
    app.run(debug=True, host = 'localhost', port=5000, threaded=True)    # Webアプリケーションの起動

 

index.html

<body>
    <h1>{{title}}</h1>
    <p>{{message}}</p>

    <div>
        <form method="post" action="/">
            <input type="text" name="form" placeholder="フォームの送信テスト" required="required">
            <input type="submit" value="送信">
        </form>
    </div>

    <footer id="footer"> 
    <ul id="flink">
        {% for remarks in data %}
            <li><a href="#">{{remarks}}</a></li>
        {% endfor %}
    </ul>
</footer>
</body>

index.htmlは「Flask入門編01」で使用したHTMLファイルの<body>タグの中身を上記のように書き換えます。

 

書き換えたら、コマンドプロンプト、ターミナルで「python app.py」と実行してみましょう。

flask02-00-01

と表示され、フォームに「Hello World!」と送信すると

flask02-00-02

送信した内容が画面に表示されていることが分かります。

 

GETとPOSTメソッドとは

ではさっそく、プログラムの説明に移っていきます。

まず、GETアクセスの「app.route」から見ていきます。

 

GETアクセスの書き方

@app.route(‘/’,methods=[‘GET’])

これはGETアクセス用のメソッドで、「methods=[‘GET’]」と記述してメソッドごとの処理を分けます。

 

GETアクセスとは簡単にいうと

‘ / ‘ のURLにアクセスしたときの処理(表示)

ということです。

今回の場合、「http://localhost:5000/」にアクセスしたときの処理・内容を書くということですね。

 

GETアクセスが ’ / ‘ アクセス時の処理を表す記述と分かれば、この下にはアクセス時に実行する処理を書けばよいとわかりますね。

なのでアクセス時はメッセージの送信を促すため、上記のようなタイトルとメッセージの文章にしています。

 

POSTアクセスの書き方

@app.route(‘/’,methods=[‘POST’])

アクセス時は「methods=[‘POST’]」と記述し、メソッドをGETアクセスと区別します。

 

POSTアクセスは簡単にいうと

対となるGETから値が送られた時の処理(表示)

ということです。

今回の場合、HTML側で記述した送信フォームから値が送信されたときに処理されることになります。(HTML側のactionでパスを設定)

 

上記プログラムでは

form = request.form[‘form’]

と記述し、Flaskモジュールの「request関数」を使用して送信された値を取得します。

ここでは「   .form[‘form’] 」と記述することで、HTML内の「name=”form”」の値を変数formに格納することができます。

 

値の受け取りができたら、変数messageに取得した内容を与え、表示させる処理を書き完成です。

 

HTML側でメソッドとパスを指定する

ここまでで、GET/POSTがどのようなに、いつ動くのかを紹介しました。

あとはHTML側でメソッドとアクションのパスを設定するだけです。

 

HTML側でGET/POSTメソッドとアクションのパスを設定するには

<form method=”メソッド” action=“パス”>

と記述します。

メソッドにはget または postが、パスには@app.routeで指定するパスを格納します。

 

今回は

<form method=”post” action=“/”>

と記述したので、「フォームの送信(値の送信)」がされたときに「http://localhost:5000/」のPOSTメソッドへ値を送ることになります。

 

この、値を送信するパスを間違えると一向に値の送受信ができないので気を付けましょう。(パスが違うことに気づかず1時間くらい悩んだこともあったので)

 

あとは、formタグの以降に送信用の入力フィールドを作成します。

送信フィールドのinputタグ中に

placeholder=”フォームの送信テスト”

required=”required”

と記述することで、入力フィールドの初期値(初期の例文)と入力必須のお知らせを追加することができます。

 

入力フィールドをつくるときには、あると便利かつ未入力を避けることができるのでオススメです。

 

これで一通りのGET/POSTメソッドが扱えるようになります。

 

Jinjaエンジンのテンプレート継承を使う

続いてJinjaエンジンの「テンプレート継承機能」について説明していきます。

このテンプレート継承を使うことで、作成したHTMLのレイアウトを引き継いで新しいページを作成することができます。

これはWebアプリを作成する上で非常に便利な機能になるので、是非この機会に復習しておきましょう。

 

継承元のレイアウトを作成する

まずプログラムの説明の前に、継承元のプログラムを見てみましょう。

先ほど紹介した「フォーム送信」用のHTMLファイルを継承元のプログラムになるように書き換えます。

 

index.html

<!DECOTYPE html>
<html lang="ja">
<head>
    <title>{%block title %}{%endblock %}</title>
    <meta charset="utf-8"/>
    <meta name="description"content="">
    <meta name="keywords"content="">
    <meta name="viewport"content="width=device-width,initial-scale=1"> <!-- モバイル最適化 -->
    <link rel="stylesheet" href="{{url_for('static', filename='style.css')}}">
</head>
<body>
    <h1>{% block headline %}{%endblock %}</h1>

    {% block content %}{% endblock %}

    {% block footer %}{% endblock %}

</body>
</html>

 

レイアウトを継承することで、HTMLの<body>タグ内が非常に見やすくなっていますね。

 

ブロックは

{% block 名前 %}{% endblock %}

と記述して指定を行います。

 

名前の部分には、そのブロックが何を示すかを書いておくと分かりやすいと思います。

上記の例では

{% block title %}{% endblock %}

として、タイトルのブロックを用意していますね。

 

このように継承元にブロックを用意するだけで、このHTMLファイルのテンプレートを使いまわすことが可能になる訳です。

実際に、テンプレートを継承してレイアウトを作成してみます。

 

作成したテンプレートを継承する

では、実際にトップページのレイアウトを記述していきます。

「index.html」ファイルの同一フォルダ内に「login.html」を追加し、下記のプログラムを記述しましょう。

 

login.html

<!-- index.htmlを継承してWebページを作成 -->
{% extends "index.html" %}

<!-- TOPページ -->
{% block title %}
{{title}}
{% endblock %}

{% block headline %}
{{title}}
{% endblock %}


{% block content %}
<p>{{ message }}</p>

<div>
    <form method="post" action="/">
        <input type="text" name="form" placeholder="フォームの送信テスト" required="required">
        <input type="submit" value="送信">
    </form>
</div>
{% endblock %}

{% block footer %}
<footer id="footer">
    <ul id="flink">
    {% for remarks in data %}
        <li><a href="#">{{remarks}}</a></li>
    {% endfor %}
    </ul>
</footer>
{% endblock %}

 

まず、レイアウトの継承をするために継承元のパスを指定する必要があります。

テンプレートを継承するには

{% extends “ファイルパス” %}

と記述します。

今回は「 {% extends “index.html” %} 」として同一フォルダ内の「index.html」を継承しました。

 

続いて継続元で記述したブロック( {% block 名前 %}{% endblock %}  )に当てはめるには

{% block 名前 %}

{{messages}}などの表示したい記述

・・・・・・・・

{% endblock %}

と記述します。

 

継承元のブロックに当てはめる形でプログラムを記述することができるので

各ページごとに表記を簡単に変えることができる

点がテンプレート継承を使う一番のメリットと言えるでしょう。

 

継承元のブロック数を増やせば増やした分だけ、記述内容も簡単に増やすことができるので使い勝手もバッチリの機能です。

FlaskでWebアプリを作成する場合は、テンプレート継承の技術は必須ですね。

 

ではログインWebアプリを作成する前にお知らせです。

PythonとFlaskを使ってWebアプリケーション開発を学びたい方は「Udemy」のこちらのコースがオススメです。

Webデザイン、アプリの開発やPythonのスキルアップを目指したい方は是非上記リンクからのぞいてみてください。

 

ログインWebアプリを作成する

上記までで、ログイン用のWebアプリを作成する知識は一通り説明をしました。

あとは、ログイン時の動的URLページの設定やログイン時の判定をプログラムするだけになります。

なので早速、プログラム全文を見ていきましょう。

今回はログイン後にフォーム送信した内容を表示していくWebアプリになります。

 

ログインWebアプリのプログラム全文

以下のファイルパスは上記の「フォーム送信」で例に出したファイルパスと同じになります。

「HTMLファイル」は「templates」フォルダ内に全て入れておきましょう。

また、index.htmlファイル、style.cssファイルは上記のHTMLファイルおよびFlask入門編01のCSSファイルと同じ内容になるので省略します。

 

app.py

from flask import Flask, render_template, request, url_for, session, redirect, abort

app = Flask(__name__)    # Flaskインスタンス作成

app.secret_key = 'secret_key'    # セッション利用の任意シークレットキー

f_data = ['サイトポリシー', 'プライバシーポリシー', 'サイトマップ']
id_data = {}    # 辞書
n_data = []    # リスト

@app.route('/', methods=['GET', 'POST'])    # ログインページ
def login_page():
    global id_data, f_data

    if request.method == 'GET':
        return render_template('login.html', \
            title='ログインページ', \
            message='IDとパスワードを入力:', \
            data = f_data)

    elif request.method == 'POST':
        id = request.form.get('id')    # 送信されたidを取得
        pswd = request.form.get('pass')    # 送信されたpassを取得

        if id in id_data:    # idがid_dataに含まれるとき、idをリストと比較
            if pswd == id_data[id]:    # pswdがidリストと一致するとき
                session['login'] = True
            else:
                session['login'] = False
        else:    # idがid_dataリストにないとき、idリストにpswdを保存
            id_data[id] = pswd
            session['login'] = True
        session['id'] = id    # セッションに送信されたid値を保管

        if session['login']:    # Trueのとき、Messagesページへ遷移
            return redirect('/user')
        else:    # Falseのとき、messageの値を変化させる
            return render_template('login.html', \
                title = 'ログインページ', \
                message='パスワードが違います', \
                data = f_data)
    else:
        return abort(404)

@app.route('/user', methods=['GET', 'POST'])    # ノートページ
def user_note():
    global n_data, f_data

    if request.method == 'GET':
        return render_template('messages.html', \
        title = "Family Note", \
        message = "ノートにメモをしよう!", \
        note_data = n_data, \
        data = f_data )
    elif request.method == 'POST':
        note = request.form.get('comment')    # 送信されたcomment値を変数へ取り出す
        n_data.append((session['id'], note))
        if len(n_data) > 15:
            n_data.pop(0)    # インデックス0番目=一番古いデータを削除
        return redirect('/user')
    else:
        return abort(404)

@app.route('/logout', methods=['GET'])    # ログアウト
def logout():
    session.pop('id', None)
    session.pop('login')    # login True Falseの破棄
    return redirect('/')

@app.route('/clear', methods=['GET'])    # メッセージの削除
def note_clear():
    n_data.clear()
    return redirect('/user')

if __name__ == '__main__':
    app.run(debug=True, host = 'localhost', port=5000, threaded=True)    # Webアプリケーションの起動

 

messages.html

<!-- index.htmlを継承してWebページを作成 -->
<!-- トップページ -->
{% extends "index.html" %}

{% block title %}
{{title}}
{% endblock %}

{% block headline %}
{{title}}
{% endblock %}

{% block content %}
<p>{{ message }}</p>
<form method = "post" action = "/user">
    <table>
        <tr>
            <th>内容</th>
            <td>
                <input type="text" name="comment" width="80" placeholder="いってらっしゃい!" required="required">    <!-- テキスト入力欄 -->
            </td>
            <td>
                <input type="submit" value="保存">    <!-- 送信ボタン -->
            </td>
        </tr>
    </table>
</form>

<label>メッセージ</label>

<ul>
<!-- メッセージログの表示欄 -->
{% for item in note_data | reverse %}
    <li>{{item[1]}} ({{item[0]}})</li>
{% endfor %}
</ul>

<div style="text-align:right;"><a href="/clear">メッセージを全て削除</a></div>    <!-- Logoutのハイパーリンク -->

<div class="logout"><a href="/logout">ログアウト</a></div>    <!-- Logoutのハイパーリンク -->

{% endblock %}

{% block footer %}
<footer id="footer">
    <ul id="flink">
    {% for remarks in data %}
        <li><a href="#">{{remarks}}</a></li>
    {% endfor %}
    </ul>
</footer>
{% endblock %}

 

login.html

<!-- index.htmlを継承してWebページを作成 -->
{% extends "index.html" %}

<!-- loginページ -->
{% block title %}
{{title}}
{% endblock %}

{% block headline %}
{{title}}
{% endblock %}

{% block content %}
<p>{{ message }}</p>

<form method="post" action="/">
<table>
    <tr>
        <th>ID</th>
        <td>
            <input type="text" name="id" value="{{id}}" placeholder="Flask 太郎" required="required">    <!-- id入力欄 -->
        </td>
    </tr>
    <tr>
        <th>パスワード</th>
        <td>
            <input type="password" name="pass" placeholder="2020" required="required">    <!-- パスワード入力欄 -->
        </td>
    </tr>
    <td>
        <input type="submit" value="ログイン">
    </td>
</table>
</form>

{% endblock %}

{% block footer %}
<footer id="footer">
    <ul id="flink">
    {% for remarks in data %}
        <li><a href="#">{{remarks}}</a></li>
    {% endfor %}
    </ul>
</footer>
{% endblock %}

 

ログインページの動作と解説

まずは「python app.py」とコマンドプロンプト、ターミナルで起動させると下図のようになります。

flask02_01

 

これは

@app.route(‘/’, methods=[‘GET’, ‘POST’]) # ログインページ

def login_page():

の部分が実行されており、「def login page(): 」下の

app.pyの一部

global id_data, f_data

if request.method == 'GET':
    return render_template('login.html', \
        title='ログインページ', \
        message='IDとパスワードを入力:', \
        data = f_data)

elif request.method == 'POST':
    id = request.form.get('id')    # 送信されたidを取得
    pswd = request.form.get('pass')    # 送信されたpassを取得

    if id in id_data:    # idがid_dataに含まれるとき、idをリストと比較
        if pswd == id_data[id]:    # pswdがidリストと一致するとき
            session['login'] = True
        else:
            session['login'] = False
    else:    # idがid_dataリストにないとき、idリストにpswdを保存
        id_data[id] = pswd
        session['login'] = True
    session['id'] = id    # セッションに送信されたid値を保管

    if session['login']:    # Trueのとき、Messagesページへ遷移
        return redirect('/user')
    else:    # Falseのとき、messageの値を変化させる
        return render_template('login.html', \
            title = 'ログインページ', \
            message='パスワードが違います', \
            data = f_data)
else:
    return abort(404)

でGET/POSTの分岐を行っています。

 

if/else で method を分岐することで GET/POSTメソッド を分岐することができ、このように記述することで

ログイン時のGET/POSTメソッドグループ

と1つのまとまりとして考えることができるので、このようなプログラムの書き方にしました。

 

elis文では、ログインID、パスワードの判定を行っており

id = request.form.get(‘id’) # 送信されたidを取得

pswd = request.form.get(‘pass’) # 送信されたpassを取得

で、送信フォームから入力されたIDとパスワードをFlaskのrequest関数を使って取得しています。
取得したIDのデータが、事前に用意したid_dataというリスト内にある場合は「すでにアカウントが存在する」ということになるので

一致した場合:login に Trueを代入してログイン

一致しない場合:loginにFalseを代入しログイン失敗

と動作します。
ここで始めて「 session[‘login’] 」という値が出てきますが、難しい物ではありません。
セッションは
利用ユーザーごとに値・情報を管理・保持するために使われる機能
で次回アクセスした際にもセッションに保管した値が破棄されるまで、保持しつづけるものです。
この機能を使うには

app.secret_key = ‘ 適当な文字列 ‘

session[  保管時のキー ] = 保管する値

と記述します。
上記プログラムでは「 session[‘id’] = id 」としてidの保管も行っています。
あとは「 login = True 」のときと、そうでないときにURLを遷移させるプログラムを記述して終了になります。
ログインエラー時には下図のように、<p>タグのメッセージ内容を変えるとログインエラーが分かりやすいと思います。

flask02_03

 

else文最後の「 abort(404) 」はhttpステータスをクライアントに返す指示で、GET/POSTメソッド以外の動作が起こった場合は404エラーを返すように指示をしています。

この部分に関しては、まだ私も理解不足ですがステータスコードを確認できるようなプログラムしておくと、エラーがどこで発生したかが分かりやすいので理解を深めたいところです。

 

また、「 login.html 」側では

<input type=”text” name=”id” value=”{{id}}” placeholder=”Flask 太郎” required=”required”>
と記述することで

flask02_02

のように

入力欄の初期表示の設定

入力必須項目へ指定

することができます。

 

送信フォームを扱う場合は、是非このような処理をしておくと良いかと思います。

 

ログイン後の動作と解説

ログイン後の動作は

@app.route(‘/user’, methods=[‘GET’, ‘POST’]) # ノートページ

として一つのルート宣言にまとめています。

 

実際にID、パスワードを入力してログインすると、ユーザーページへ移動していることが分かります。

flask02_04

 

ノートのメモということで、内容を保存すると

flask02_05

のようにメッセージ内容が表示されます。

 

これは

elif request.method == ‘POST’:

note = request.form.get(‘comment’) # 送信されたcomment値を変数へ取り出す

の部分で、送信された値をnoteという変数へ取り出しているからであり、これをコンテンツブロックの「messages」に格納することで内容を表示しています。

 

ノート用のルート下にでてくる

if len(n_data) > 15:

n_data.pop(0) # インデックス0番目=一番古いデータを削除

はメッセージ内容が15個を超えたら、一番古いデータから削除を行う記述になります。

 

Python にはpop()関数があり、

()内に指定した配列番号の値を削除して、配列を前へ詰める

ことができます。

このpop()関数を使用することでデータの管理をしているということです。

 

上記のように、Pythonの機能を使えばデータの削除も簡単に行えるので、

@app.route('/clear', methods=['GET'])    # メッセージの削除
def note_clear():
    n_data.clear()
    return redirect('/user')

「 .clear() 」関数を使ってメッセージの全削除もできてしまいます。

 

flask02_07

 

以上、ログインWebアプリの動作・解説になります。

 

参考にした書籍

※ p115~ログインページの作成を参考に、改良・改善

 

参考にしたサイト

Qiita:Python Flask, GET, POST基本動作メモ

 

Qiita:flaskでhttpステータスを返却する方法

 

Flaskのエラーハンドラメモ:ようへいの日々精神XP

 

note.nkmk.me:Pythonでリストの要素を削除するclear,pop,remove,del

 

始めは理解するのに手こずると思いますが、プログラムを自分で書いてアウトプットしていくうちに「ああ!そういうことか!」と分かってくると思うので、上記のプログラムを自分でオリジナル風に書き換えて学ぶというのもアリかと思います。

お疲れ様でした。

flask02_top_thumbnail
学びに関する情報をチェック!