見出し画像

【第8回】FlaskでWebアプリ作成練習。ログイン機能(だけ)を実装しよう。

こんにちは(@t_kun_kamakiri)

前回↓に引き続き、「Flask」の勉強を進めていきたいと思います。

ちょっとFlaskの勉強の期間が空いたのですが、時間がたっぷりできたのでちょっとずつ勉強を再開しています。

FlaskといえばPythonを使ってWebブラウザを操作するフレームワークですが、結構簡単に使うことができます。

僕自身は、本日が「第8回」なので8日間くらいしか勉強していないという事ですが、ログイン機能(ログイン機能しかない)くらいなら実装することができます。

今日やるのは「Flaskを使ってログイン機能を実装する」です。

イメージは以下のです。

画像1

●idとパスワードを入力する

●はじめて入力するidだったら新規追加とする。
この時データを辞書型として追加していく。

●同じidを入力した場合、前回入力したパスワードと一致していない場合は「パスワードが間違っています」と警告を出す。

超シンプルで、ログイン機能以外何もない(何の役にも立たない)ものですが、Flaskの勉強にはとても理解がしやすいと思います。

他の言語でも簡単にできる(例えばPHPとか)のですが、今後Pythonのライブラリを使いたいのでWebと連携して勉強するためにFlaskを使って感覚を養う目的です。

では、少しずつコードを書きながら解説します。

☟こちらの参考書で勉強しています。

ファイル構成

全体のファイル構成は以下のようにしておきます。

--templates
  |--layout.html
  |--login.html
--static
  |--style.css
run.py

●「run.py」がPythonのコード
●「~.html」がhtmlのファイル
●「style.css」がCSSのファイル(今回は使っていません)

ログイン画面のhtmlを用意する

まずは、ログイン画面用のhtmlを用意しておきます。

ここでは、ページアウトのレイアウトを継承するでも紹介したように、htmlのテンプレートを用意しておきます。

layout.html

<!doctype html>
<html lang="ja">
<head>
   <title>{% block title %}{% endblock %}</title>
   <meta charset="utf-8"/>
   <link rel="stylesheet" 
       href="{{url_for('static', filename='style.css')}}">
</head>
<body>
   <h1>{% block headline %}{% endblock %}</h1>


   {% block content %}{% endblock %}
   
   <div class="footer">
       {% block footer %}{% endblock %}
   </div>
</body>
</html>

これがhtnlのひな型です。
こちらはテンプレートとして使うだけで実際はほとんど書き換えることはしません。

login.html

{% extends "layout.html" %}

{% block title %}
ログイン
{% endblock %}

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

{% block content%}

<p>{{message}}</p>

<form method="post" action="/login">
   <table>
       <tr>
           <th>id</th>
           <td>
               <input type="text" name="id" value="{{id}}"> 
           </td>
           <th>password</th>
           <td>
               <input type="password" name="pass">
           </td>
       </tr>
       <th></th>
       <td>
           <input type="submit" value="Login">
       </td>
   </table>
</form>
{% endblock %}

run.py

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

app = Flask(__name__)
app.secret_key = b'random string...'

@app.route('/', methods=['GET'])
def index():
   title ="メッセージ"
   msg = "はじめてのFlask"
   return render_template('login.html', \
           title='Messages', \
           message=msg)
   # return redirect('/')


if __name__ == '__main__':
  app.run()

ここまででどうなっているのかを確認してみましょう。

$python3 run.py

画像2

※実は今日からvisual stdio codeとWSL(Windows上でLinuxを使う)を連携しています。

「http://127.0.0.1:5000/」にアクセスすると以下のようにブラウザ上に表示されます。

画像3

ひとまずここまでできました。

試しに「id」と「password」に何か入れてみてください。
エラーが出ると思います。

これはまだPOST処理について何も指定していないからです。

画像4

どういう状況かというと、「/」にアクセスがあった場合には「login.html」を表示するようにしています。
そして、「id」と「password」を入力すると入力した変数は黄色枠で囲んだ「"id"」「"pass"」として、formタグのactionで指定している「/login」に変数を返すのです・・・・が、「/login」にアクセスがあったときの処理をPythonで記述していないからエラーが出ました。

「/login」にアクセスがあった場合の処理を書く


run.pyに以下を追加します。(処理は後から書いていきます)

@app.route('/login' , methods=['POST'])
def login_post():

これで「/login」にアクセスがあったときの処理を指定できます。

処理の中身ですが、

●新規のidだったらname_listの辞書型に「id」と「pass」を追加する

●既に登録しているidだったらパスワードが一致しているかを確認する

●既に登録しているidでパスワードが一致しなかった場合は、「パスワードが一致していません」と警告を出す

という場合分けをします。

全体の「run.py」を書いておきます。

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

app = Flask(__name__)
app.secret_key = b'random string...'

name_list = {}

@app.route('/', methods=['GET'])
def index():
   global name_list

   title ="メッセージ"
   msg = "はじめてのFlask"
   return render_template('login.html', \
           title='Messages', \
           message=msg,
           name = name_list)
   return redirect('/')

@app.route('/login' , methods=['POST'])
def login_post():
   global name_list

   id = request.form.get('id')
   pwd = request.form.get('pass')

   #idが既にあるかどうかを確認する
   if id in name_list:
       #IDとpasswordが一致しているかを確認する。
       if pwd == name_list[id]:
           #ログイン可能な状態にする
           session['flag'] = True
       #IDとpasspwrdが一致しない場合は
       else:
           session['flag'] = False
   #idの新規登録の場合
   else:
       name_list[id] = pwd
       session['flag'] = True #ログイン可能な状態にする

   session['id'] = id
   user_id = session.get("id")
   #idとpasseprdが一致するかどうかの確認
   if session['flag']: #一致した場合
       return redirect('/')
   else: #一致しなかった場合
       title = 'ログイン'
       msg = 'パスワードが間違っています'
       return render_template('login.html',\
           title = title,\
           message = msg,\
           name = name_list,\
           user_id=user_id)
   

if __name__ == '__main__':
   # app.run(debug=True)
   app.run()


●新規のidだったらname_listの辞書型に「id」と「pass」を追加する

if id in name_list:

で確認します。

ちょっとわかりにくいので、別のファイルとかでPythonコードを以下のように書いて確認をしてみればわかります。

id = 'kamakiri'
name_list = {
   'kamakiri':'kkkk',
   'yamada':'kkga',
   'suzuki':'aaaa'
}

if id in name_list:
 print(id)

#結果
kamakiri

「id = 'kamakiri'」が「name_list」の中に存在していれば、name_listのキーを返してくれます。

もし、新規のidだったら辞書型に追加するようにします。

id = 'apple'
id_pass = 'apple_pwd'

name_list = {
   'kamakiri':'kkk',
   'yamada':'kkga',
   'suzuki':'aaaa'
}

if id in name_list:
 print(id)

else:
 name_list[id] = id_pass
 print(name_list) 

#結果
{'kamakiri': 'kkk', 'yamada': 'kkga', 'suzuki': 'aaaa', 'apple': 'apple_pwd'}

さらに、flaskのsessionを使えば、session[キー] = 値という形で、キーと値を保管することができます。

以下のものだけを指定しています。
●session['flag'] = True ←ログイン可能かどうかの判断
●session['id'] = id ←idをWebサーバー上に一旦保存しておく
※こうすることで違うブラウザ上でアクセスしてもidが保存されているので、ユーザー間でidが被ることはありません。

●既に登録しているidだったらパスワードが一致しているかを確認する

       #IDとpasswordが一致しているかを確認する。
       if pwd == name_list[id]:
           #ログイン可能な状態にする
           session['flag'] = True
       #IDとpasspwrdが一致しない場合は
       else:
           session['flag'] = False

session['flag'] のフラグが「True」か「False」だけでログイン可能かどうかの確認を行っています。

●既に登録しているidでパスワードが一致しなかった場合は、「パスワードが一致していません」と警告を出す

if session['flag']: #一致した場合
       return redirect('/')
   else: #一致しなかった場合
       title = 'ログイン'
       msg = 'パスワードが間違っています'
       return render_template('login.html',\
           title = title,\
           message = msg,\
           name = name_list,\
           user_id=user_id)

「id」と「パスワード」が一致していたら無事ログインが可能ということで「return redirect('/')」にアクセスを返しています。

今は、

@app.route('/', methods=['GET'])
def index():

の処理が実行されるだけです。

「id」と「パスワード」が一致していなかったら、

       title = 'ログイン'
       msg = 'パスワードが間違っています'
       return render_template('login.html',\
           title = title,\
           message = msg,\
           name = name_list,\
           user_id=user_id)

という処理が実行されます。

ここで使われている「name_list」や「session」がわかりにくいのでWeb上に出力してどういう風に動作しているのかを確認してみます。

●「 session['id'] = id」でsessionの'id'というキーに「login.html」から取得したidという値をすることができて、

●「user_id = session.get("id")」とすると、sessionの'id'に対応した値を取得できます。今は、それをuser_idに代入しています。

   session['id'] = id
   user_id = session.get("id")

これをlogin.htmlが読み込まれたときに値を引き渡せばよいので、

        return render_template('login.html',\
           title = title,\
           message = msg,\
           name = name_list,\
           user_id=user_id)

と書いておきました。

「name 」と「user_id」を「layout.html」に書いておけば、「login.html」の表示の際にWeb上で確認することができます。

layout.html

   <h1>{% block headline %}{% endblock %}</h1>
   <p>{{name}}</p>
   <p>{{user_id}}</p>

というわけで完成しました(^^)/

全体のコードを載せておきます

断片的な説明だとわかりにくいと思うので全体のコードを載せておきます。

【ファイル構成】

--templates
  |--layout.html
  |--login.html
--static
  |--style.css
run.py

layout.html

<!doctype html>
<html lang="ja">
<head>
   <title>{% block title %}{% endblock %}</title>
   <meta charset="utf-8"/>
   <link rel="stylesheet" 
       href="{{url_for('static', filename='style.css')}}">
</head>
<body>
   <h1>{% block headline %}{% endblock %}</h1>
   <p>{{name}}</p>
   <p>{{user_id}}</p>


   {% block content %}{% endblock %}
   
   <div class="footer">
       {% block footer %}{% endblock %}
   </div>
</body>
</html>

login.html

{% extends "layout.html" %}

{% block title %}
ログイン
{% endblock %}

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

{% block content%}

<p>{{message}}</p>

<form method="post" action="/login">
   <table>
       <tr>
           <th>id</th>
           <td>
               <input type="text" name="id" value="{{id}}"> 
           </td>
           <th>password</th>
           <td>
               <input type="password" name="pass">
           </td>
       </tr>
       <th></th>
       <td>
           <input type="submit" value="Login">
       </td>
   </table>
</form>
{% endblock %}

run.py

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

app = Flask(__name__)
app.secret_key = b'random string...'

name_list = {}

@app.route('/', methods=['GET'])
def index():
   global name_list

   title ="メッセージ"
   msg = "はじめてのFlask"
   return render_template('login.html', \
           title='Messages', \
           message=msg,
           name = name_list)
   return redirect('/')

@app.route('/login' , methods=['POST'])
def login_post():
   global name_list

   id = request.form.get('id')
   pwd = request.form.get('pass')

   #idが既にあるかどうかを確認する
   if id in name_list:
       #IDとpasswordが一致しているかを確認する。
       if pwd == name_list[id]:
           #ログイン可能な状態にする
           session['flag'] = True
       #IDとpasspwrdが一致しない場合は
       else:
           session['flag'] = False
   #idの新規登録の場合
   else:
       name_list[id] = pwd
       session['flag'] = True #ログイン可能な状態にする

   session['id'] = id
   user_id = session.get("id")
   #idとpasseprdが一致するかどうかの確認
   if session['flag']: #一致した場合
       return redirect('/')
   else: #一致しなかった場合
       title = 'ログイン'
       msg = 'パスワードが間違っています'
       return render_template('login.html',\
           title = title,\
           message = msg,\
           name = name_list,\
           user_id=user_id)
   

if __name__ == '__main__':
   # app.run(debug=True)
   app.run()

Pythonをじ

$python3 run.py

画像2

これで以下のようなWeb上の動作になると思います。

画像6

完成です(^^)/

まだまだわかっていないことが多いので、思いもよらない動作をすることがあるかもしれません。

こうやってわかった部分をnoteにまとめていくと理解が深まるので良いですね(^^)

本日でFlaskの基本的なことは終わったので、以降は「デザイン」を整えながらかっちょいい良いアプリ作成をしていきます。

☟おすすめの参考書はこちらです。

Twitter➡@t_kun_kamakiri
ブログ➡宇宙に入ったカマキリ(物理ブログ)

いいなと思ったら応援しよう!

ピックアップされています

Flask入門練習日記 Pythonフレームワーク

  • 16本

コメント

コメントするには、 ログイン または 会員登録 をお願いします。
【第8回】FlaskでWebアプリ作成練習。ログイン機能(だけ)を実装しよう。|物理・プログラミング・Python、Web制作・自己啓発
word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word

mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1