こんにちは。
プログラミングを勉強しているロペです。
PythonのFlaskでGET/POSTを使用せず、Vue.jsのAjaxでSPA(Single Page Application)でログインできる機能をプログラムしてみたので、その方法を纏め記事にしました。
↓の動画の機能をプログラムしてみました。
(フロント側はVue.jsを使用しています。)
ログイン画面
↓
モーダルダイアログ
の順で説明していきます。
この記事が誰かの役に立ったら嬉しいです。
SPA(Single Page Application)とは
最初に、SPAについて軽く説明をしておきます。
SPA:Single Page Applicationとは、「1枚のWebページで全て完結する」という意味になります。
最近よく見るWebページは、何か操作するたびにフォームを送信してページをリロードするみたいなのは少なくなってきています。
フォーム送信することも、ページを移動することもなく、その場でダイナミックにページの内容が書き換わる方が楽なため、そういったWebページが増えています。
(このようなアプリをSPAと呼んでいます)
そして、これはJavascriptベース書くことで実現できます。
FlaskとJinjaテンプレートでSPAは困難なので、JavascriptとVue.jsのMustacheテンプレートで記述します。
ログイン画面
全部のコードは一番下に載せておきますので、
この箇所ではポイントのみに絞って説明します
↓は実行画面(この画面を実現しているコードの説明をします)
サーバー側(Flaskスクリプト)のポイント
@app.route(‘/’, methods=[‘GET’])
まず、このコードから続くコードでログイン画面を作っています。
クライアント側(HTML)のポイント
<a href=”javascript:void(0)” v-on:click=”in_out_check” style=”font-size: 30px;”>
このコードでクリックして、モーダルダイアログに飛べるようにしています。
モーダルダイアログ
↓は実行画面(この画面を実現しているコードの説明をします)
サーバー側(Flaskスクリプト)のポイント
@app.route('/login', methods=['POST']) def login_post(): global member_data, message_data id=request.form.get('id') pswd =request.form.get('pass') if id in member_data: if pswd == member_data[id]: flg = 'True' else: flg = 'False' else: member_data[id] = pswd flg ='True' try: with open(member_data_file,'wb') as f: pickle.dump(member_data, f) except: pass return flg
login_post
ログインの処理です。送られてきたIDとパスワードをチェックして、ログインできればTrue、できなければFalseを返します。あた新たにIDを追加した場合は、member_dataをファイルに保存しています。
pickleによるオブジェクトの保存
今回のスクリプトでは、リストと辞書をファイルに読み書きするのに「pickle」というモジュールを利用しています。これは、Pythonのオブジェクトをファイルに保存するに使用されるもになります。
ファイルにオブジェクトを保存する方法
with open(ファイル名,’wb’) as f:
pickle.dump(オブジェクト,f)
ファイルからオブジェクトをロードする方法
with open(ファイル名, “rb”) as f:
変数 = pickle.load(f)
クライアント側(HTML)のポイント
FormData
フォームの情報は「FormData」というオブジェクトにまとめて用意しています。
login: function(e){ let formData = new FormData(); formData.append("id", this.form_id); formData.append("pass", this.form_pass); let self = this;
v-modelでform_idとform_passにバインドしています。
Ajaxメソッド
クライアント側からサーバーへのアクセスはAjaxを利用しています。
$.ajax({ type:'POST', url:'/login', data: formData, processData: false, contentType: false, success: function (data) { if (data == 'True'){ self.in_out ='logout'; self.login_flg = true; self.login_id = self.form_id; self.alert_msg = 'Loginできました!'; self.form_id=''; self.form_pass=''; $('#login').modal('hide'); // self.getMsg(); } else{ self.login_msg = '※IDもしくはパスワードが一致しません'; self.pass=''; } }, error: function(request, status, err){ self.login_msg='※問題が発生しました'; } });
アクセスに成功する場合は、「success」というコールバック関数で処理しています。
ヒス数には、サーバーから受け取った値が渡されるので、これを使用して表示を更新したりしています。
なお、Ajxaの詳細にについては、別の記事で書いているので良ければ覗いてみてください。
唐突ですが、広告挟みます。
転職を考えている方は無料で登録できますので、登録してみてはいかがでしょうか?
(私も転職をしており、転職エージェントの方にはお世話になったので、お勧めです)
全プログラム(コード)
上記では、かなりポイントを絞ってかきましたので、コード全文を載せておきます。
Flask
まず、Flaskのスクリプトです。
from flask import Flask, render_template, request, session, url_for, redirect, jsonify from flask.views import MethodView import pickle app = Flask(__name__) app.secret_ley = b'random string...' member_data = {} message_data =[] member_data_file = 'member_data.dat' #load member_data from file. try: with open(member_data_file, "rb") as f: list = pickle.load(f) if list !=None: member_data = list except: pass #access top page. @app.route('/', methods=['GET']) def index(): global message_data return render_template('index4.html',\ login=False,\ title='Sample',\ message='not logined...', data=message_data ) # login form sended. @app.route('/login', methods=['POST']) def login_post(): global member_data, message_data id=request.form.get('id') pswd =request.form.get('pass') if id in member_data: if pswd == member_data[id]: flg = 'True' else: flg = 'False' else: member_data[id] = pswd flg ='True' try: with open(member_data_file,'wb') as f: pickle.dump(member_data, f) except: pass return flg if __name__ == '__main__': app.debug=True app.run()
HTML
次にHTMLです
{% extends "layout2.html" %} {% block title %} login-app {% endblock %} {% block headline %} {{title}} {% endblock %} {% block content %} <div id="app" class="m-3"> <message_board /> </div> {% raw %} <script type="text/x-template" id="message_board-template"> <div> <div class="test-right h6"> <h5>{{ alert_msg }}</h5> <br /> <a href="javascript:void(0)" v-on:click="in_out_check" style="font-size: 30px;"> {{in_out}}</a> </div> <!-- Login Dialog --> <div class="modal fade" id="login"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="card"> <div class="card-header"> <h4 class="card-title"> {{login_msg}} </h4> </div> <div class="card-body"> <div class="form-group"> <label for="id">ID</label> <input type="text" class="form-control" id="id" name="id" v-model="form_id"> <small class="form-text text-muted"> ※IDを入力 <br /> (未登録の場合は登録したいIDを記入) </small> </div> <div class="form-group"> <label for="pass">Password</label> <input type="password" class="form-control" id="pass" name="pass" v-model="form_pass"> <small class="form-text text-muted"> ※パスワードを入力 <br /> (未登録の場合は登録するPasswordを記入) </small> </div> <div class="form-group"> <button class="btn btn-primary" v-on:click="login">Login</button> </div> </div> </div> </div> </div> </div> </div> </script> {% endraw %} <script> // mycomp_board object Vue.component('message_board',{ template:'#message_board-template', data: function(){ return{ in_out:'※Loginはここから', login_flg:false, login_id:'', form_id:'',form_pass:'', login_msg:'Login form:', alert_msg:'Loginしてください。', post_msg:'', msg_data:[], } }, methods:{ // check login/logout. in_out_check: function(e){ if(this.login_flg){ this.logout(); } else{ this.show_login(); } }, //show login dialog show_login:function(){ this.form_id=''; this.form_pass=''; this.login_msg='Login Form:'; $('#login').modal('show'); }, // access server and logined. login: function(e){ let formData = new FormData(); formData.append("id", this.form_id); formData.append("pass", this.form_pass); let self = this; $.ajax({ type:'POST', url:'/login', data: formData, processData: false, contentType: false, success: function (data) { if (data == 'True'){ self.in_out ='logout'; self.login_flg = true; self.login_id = self.form_id; self.alert_msg = 'Loginできました!'; self.form_id=''; self.form_pass=''; $('#login').modal('hide'); // self.getMsg(); } else{ self.login_msg = '※IDもしくはパスワードが一致しません'; self.pass=''; } }, error: function(request, status, err){ self.login_msg='※問題が発生しました'; } }); }, // logout. logout: function(){ console.log('logout'); this.in_out = 'login'; this.login_flg = false; this.login_id=''; this.alert_msg='Logoutしました' }, }, }); // start Vue. new Vue({ el:'#app', }); </script> {% endblock %} {% block footer %} <h6>Coryright 2020 Rope_blog </h6> {% endblock %}
<!doctype html> <html lang="ja"> <head> <title>{%block title %}{% endblock %}</title> <meta charset="utf-8"/> <meta name="vieport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="{{url_for('static',filename='style.css')}}"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" ></script> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script> </script> </head> <body> <div class="test" style="background-color:rgba(169, 84, 177, 0.055);"> <div class="target"> <div class="container"style="max-width: 100%;"> <div style="display: table;width:100%;"> <div style="display:table-cell; vertical-align:middle;font-size: 30px;"> <div class="text-left" style="border-bottom:5px solid rgb(135, 250, 196);" > <h1 class="display-3"> <div style="font-size: 50px;"> {% block headline %}{% endblock %} </div> </h1> </div> <div style="border-bottom:5px solid rgb(135, 250, 196);" > {% block content %}{% endblock %} </div> <div class="text-right"> {% block footer %}{% endblock %} </div> </div> </div> </div> </div> </div> </body> </html>
コメント