【Python】FullCalendarにログイン機能をつけてみる
おはようございます。
今回はログイン画面を追加して、
ユーザー毎にカレンダーを持てるようにしてみました。
ちょっと長くなってしまったので、ソースはそのうち Github のあげる予定です。
プログラムは前回のものを流用します。
スポンサーリンク
テーブルの追加、修正
ユーザ、パスワード用にそれぞれテーブルを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | -- MSTパスワード createtableMST_PASSWORD( USER_CDvarchar(20)not nullcomment'ユーザーコード' ,PASSWORDvarchar(128)not nullcomment'パスワード' ,CREATE_USERvarchar(20)comment'作成者' ,CREATE_DATEdatetimecomment'作成日時' ,UPDATE_USERvarchar(20)comment'更新者' ,UPDATE_DATEdatetimecomment'更新日時' ,constraintMST_PASSWORD_PKCprimary key(USER_CD) )comment'MSTパスワード'; -- MSTユーザー createtableMST_USER( USER_CDvarchar(20)not nullcomment'ユーザーコード' ,USER_NAMEvarchar(60)not nullcomment'ユーザー名' ,MAIL_ADDRESSvarchar(128)comment'メールアドレス' ,DEFAULT_COLORvarchar(8)default'#FFFFFF'comment'デフォルト背景色:#RGB形式' ,CREATE_USERvarchar(20)comment'作成者' ,CREATE_DATEdatetimecomment'作成日時' ,UPDATE_USERvarchar(20)comment'更新者' ,UPDATE_DATEdatetimecomment'更新日時' ,constraintMST_USER_PKCprimary key(USER_CD) )comment'MSTユーザー'; -- TBLスケジュール createtableTBL_SCHEDULE( USER_CDvarchar(20)not nullcomment'ユーザコード' ,IDint(10)not nullcomment'ID' ,TITLEvarchar(100)comment'タイトル' ,STARTdatetimecomment'開始日時' ,ENDdatetimecomment'終了日時' ,TEXTCOLORvarchar(20)comment'文字色' ,COLORvarchar(20)default'#FFFFFF'comment'背景色:#RGB形式' ,URLvarchar(100)comment'URL' ,ALLDAYint(1)default0comment'終日フラグ:0:終日/1:時間指定' ,DESCRIPTIONvarchar(1000)comment'説明' ,CREATE_USERvarchar(20)comment'作成者' ,CREATE_DATEdatetimecomment'作成日時' ,UPDATE_USERvarchar(20)comment'更新者' ,UPDATE_DATEdatetimecomment'更新日時' ,constraintTBL_SCHEDULE_PKCprimary key(USER_CD,ID) )comment'TBLスケジュール'; |
テーブルを作成したら、適当にユーザーの情報をインサートしておきましょう。
画面の追加、修正
CSSの追加
ログイン画面用のCSSを追加
login.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | body { background:#ffbb55nonerepeatscroll00; } .jumbotron { text-align:center; width:35rem; border-radius:0.5rem; top:0; bottom:0; left:0; right:0; position:absolute; margin:4remauto; background-color:#fff; padding:2rem; height:45rem; } .container .glyphicon-list-alt { font-size:10rem; margin-top:3rem; color:#f96145; } input { width:100%; margin-bottom:1.4rem; padding:1rem; background-color:#ecf2f4; border-radius:0.2rem; border:none; } h2 { margin-bottom:3rem; font-weight:bold; color:#ababab; } .btn { border-radius:0.2rem; } .btn .glyphicon { font-size:3rem; color:#fff; } .full-width { background-color:#8eb5e2; width:100%; -webkit-border-top-right-radius:0; -webkit-border-bottom-right-radius:0; -moz-border-radius-topright:0; -moz-border-radius-bottomright:0; border-top-right-radius:0; border-bottom-right-radius:0; } .box { position:absolute; bottom:0; left:0; margin-bottom:3rem; margin-left:3rem; margin-right:3rem; } span.errMsg { color:#f96145; font-size:11px; } |
ログイン画面の追加
Login.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>カレンダーサンプル - ログイン</title> <link rel="stylesheet"href="https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <link rel="stylesheet"href="{{ static_url('css/login.css') }}"/> <script type="text/javascript"src="https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> <script type="text/javascript"src="http://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> <script type="text/javascript"> functionsubmitLogin(){ $("#loginForm").submit(); } </script> </head> <body> <form id="loginForm"method="post"action="/login"> <div class="jumbotron"> <div class="container"> <span class="glyphicon glyphicon-list-alt"></span> <h2>カレンダーサンプル</h2> <div class="box"> {% module xsrf_form_html() %} <input id="inputUserCd"name="user_cd"type="text"placeholder="ユーザーコード"> <input id="inputPassword"name="password"type="password"placeholder="パスワード"> <span class="errMsg">{{ error_msg }}</span> <button class="btn btn-default full-width"onclick="submitLogin()"> <span class="glyphicon glyphicon-ok"></span> </button> </div> </div> </div> </form> </body> </html> |
プログラムの修正
ユーザ情報取得用メソッド追加
データベースからユーザのパスワードや名前を取得するメソッドを追加
MySQLUtil.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | defget_user_password(self,user_cd=""): ''' ユーザーコードからパスワードを取得 :param user_cd: :return: ''' withclosing(mysql.connector.connect(**self.config))asconn: c=conn.cursor(dictionary=True) sql="SELECT PASSWORD FROM MST_PASSWORD WHERE USER_CD = '"+user_cd+"'" c.execute(sql) result=c.fetchone() ifresult==None: returnNone returnresult[r"PASSWORD"] defget_user_name(self,user_cd=""): ''' ユーザーコードからユーザ名を取得 :param user_cd: :return: ''' withclosing(mysql.connector.connect(**self.config))asconn: c=conn.cursor(dictionary=True) sql="SELECT USER_NAME FROM MST_USER WHERE USER_CD = '"+user_cd+"'" c.execute(sql) result=c.fetchone() ifresult==None: returnNone returnresult[r"USER_NAME"] |
認証用クラス/メソッド追加
変更点
1. RequestHandlerを継承した認証用のクラスを追加
2. 認証処理を実施するクラスを追加
3. ログアウト用のクラスを追加
4. 「@tornado.web.authenticated」アノテーションでチェックをするように
Main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | classAuthBaseHandler(tornado.web.RequestHandler): ''' 認証ハンドラー基底クラス ''' cookie_user_cd="user_cd" defget_current_user(self): logging.info("AuthBaseHandler [get_current_user]") user_cd=self.get_secure_cookie(self.cookie_user_cd) ifnotuser_cd: return"" returnuser_cd.decode("UTF-8") defset_current_user(self,user_cd): logging.info("AuthBaseHandler [set_current_user]") self.set_secure_cookie(self.cookie_user_cd,user_cd) defclear_current_user(self): logging.info("AuthBaseHandler [clear_current_user]") self.clear_cookie(self.cookie_user_cd) classAuthLoginHandler(AuthBaseHandler): ''' ログインハンドラー ''' defget(self): logging.info("AuthLoginHandler [get]") self.render("Login.html",error_msg="") defpost(self): logging.info("AuthLoginHandler [post]") self.check_xsrf_cookie() # 認証処理 input_user_cd=self.get_argument("user_cd") input_password=self.get_argument("password") mysql=MySQLUtil() password=mysql.get_user_password(input_user_cd) # 入力されたパスワードと保存されているパスワードをチェック is_auth=Falseifinput_password!=password elseTrue ifis_auth: self.set_current_user(input_user_cd) self.redirect("/main") else: self.render("Login.html",error_msg="ユーザーコードまたはパスワードが間違っています。") classAuthLogoutHandler(AuthBaseHandler): defget(self): self.clear_current_user() self.redirect('/login') |
カレンダー取得処理の修正
Main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | classGetCalendar(AuthBaseHandler): """ カレンダー取得 """ definitialize(self): logging.info("GetCalendar [initialize]") @tornado.web.authenticated defget(self): logging.info("GetCalendar [get]") mysql=MySQLUtil() arg=self.request.arguments start=arg["start"][0].decode("UTF-8") end=arg["end"][0].decode("UTF-8") user_cd=self.get_current_user() data=mysql.get_schedule(start,end,user_cd) json_data=json.dumps(data,default=support_datetime_default) self.write(json_data) URLハンドリングの追加 Main.py app=tornado.web.Application([ (r"/login",AuthLoginHandler), (r"/logout",AuthLogoutHandler), (r"/main",MainHandler), (r"/getCalendar",GetCalendar), (r"/regist",RegistSchedule), (r"/update",UpdateSchedule), (r"/delete",DeleteSchedule), ], template_path=os.path.join(os.getcwd(),"templates"), static_path=os.path.join(os.getcwd(),"static"), login_url="/login", cookie_secret="adfaskljfwepmaldskf:as;k", xsrf_cookies=True ) |
非同期通信時のXSRF設定
Ajax通信でXSRFパラメータを送信するように
script.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | /** * リクエスト送信. */ functionsendAjaxRequest(method,eventData){ varcal=$("#calendar").fullCalendar("getView"); eventData.searchStart=cal.start; eventData.searchEnd=cal.end; // 処理名を設定 varmethodName="登録"; if(method=="update"){ methodName="更新" }elseif(method=="delete"){ methodName="削除" } $.ajax({ url:"http://localhost:8080/"+method, type:"POST", headers:{'X-XSRFToken':$("*[name=_xsrf]")[0].value}, data:JSON.stringify(eventData), success:function(jsonResponse){ // カレンダー再描画 $('#calendar').fullCalendar('removeEvents'); $('#calendar').fullCalendar('renderEvents',$.parseJSON(jsonResponse)) $('#inputScheduleForm').modal('hide'); alert("予定を"+methodName+"しました。"); }, error:function(){ alert("予定の"+methodName+"に失敗しました。"); } }); $('#calendar').fullCalendar('unselect'); } |
起動してみる
ヘッダーを追加して、ユーザ名を表示、あとは適当にメニューを追加してみました。
まとめ
ひとまず、ここまででカレンダーは終わりにしようかなと思ってます。
そのうちなんかサービスが作れたらいいな。
ソースは整理して別途 Github にアップする予定です。
何かの参考になれば。
ではでは。
ディスカッション
コメント一覧
まだ、コメントがありません