BETA

【Python】FullCalendarにログイン機能をつけてみる

投稿日:2019-01-15
最終更新:2019-01-15
※この記事は外部サイト(https://www.doraxdora.com/blog/2018/07/16/...)からのクロス投稿です

今回はログイン画面を追加して、
ユーザー毎にカレンダーを持てるようにしてみました。

ちょっと長くなってしまったので、ソースはそのうち Github のあげる予定です。

プログラムは前回のものを流用します。

【Python】FullCalendarの週、日表示から操作できるようにする

テーブルの追加、修正


ユーザ、パスワード用にそれぞれテーブルを追加します。
-- MSTパスワード  
create table MST_PASSWORD (  
  USER_CD varchar(20) not null comment 'ユーザーコード'  
  , PASSWORD varchar(128) not null comment 'パスワード'  
  , CREATE_USER varchar(20) comment '作成者'  
  , CREATE_DATE datetime comment '作成日時'  
  , UPDATE_USER varchar(20) comment '更新者'  
  , UPDATE_DATE datetime comment '更新日時'  
  , constraint MST_PASSWORD_PKC primary key (USER_CD)  
) comment 'MSTパスワード' ;  

-- MSTユーザー  
create table MST_USER (  
  USER_CD varchar(20) not null comment 'ユーザーコード'  
  , USER_NAME varchar(60) not null comment 'ユーザー名'  
  , MAIL_ADDRESS varchar(128) comment 'メールアドレス'  
  , DEFAULT_COLOR varchar(8) default '#FFFFFF' comment 'デフォルト背景色:#RGB形式'  
  , CREATE_USER varchar(20) comment '作成者'  
  , CREATE_DATE datetime comment '作成日時'  
  , UPDATE_USER varchar(20) comment '更新者'  
  , UPDATE_DATE datetime comment '更新日時'  
  , constraint MST_USER_PKC primary key (USER_CD)  
) comment 'MSTユーザー' ;  

-- TBLスケジュール  
create table TBL_SCHEDULE (  
  USER_CD varchar(20) not null comment 'ユーザコード'  
  , ID int(10) not null comment 'ID'  
  , TITLE varchar(100) comment 'タイトル'  
  , START datetime comment '開始日時'  
  , END datetime comment '終了日時'  
  , TEXTCOLOR varchar(20) comment '文字色'  
  , COLOR varchar(20) default '#FFFFFF' comment '背景色:#RGB形式'  
  , URL varchar(100) comment 'URL'  
  , ALLDAY int(1) default 0 comment '終日フラグ:0:終日/1:時間指定'  
  , DESCRIPTION varchar(1000) comment '説明'  
  , CREATE_USER varchar(20) comment '作成者'  
  , CREATE_DATE datetime comment '作成日時'  
  , UPDATE_USER varchar(20) comment '更新者'  
  , UPDATE_DATE datetime comment '更新日時'  
  , constraint TBL_SCHEDULE_PKC primary key (USER_CD,ID)  
) comment 'TBLスケジュール' ;

テーブルを作成したら、適当にユーザーの情報をインサートしておきましょう。

画面の追加、修正


CSSの追加

ログイン画面用のCSSを追加

login.css

body {  
    background: #ffbb55 none repeat scroll 0 0;  
}  

.jumbotron {  
   text-align: center;  
   width: 35rem;  
   border-radius: 0.5rem;  
   top: 0;  
   bottom: 0;  
   left: 0;  
   right: 0;  
   position: absolute;  
   margin: 4rem auto;  
   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
<!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">  
            function submitLogin() {  
                $("#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

def get_user_password(self, user_cd=""):  
    '''  
    ユーザーコードからパスワードを取得  
    :param user_cd:  
    :return:  
    '''  

    with closing(mysql.connector.connect(**self.config)) as conn:  

        c = conn.cursor(dictionary=True)  

        sql = "SELECT PASSWORD FROM MST_PASSWORD WHERE USER_CD = '" + user_cd + "'"  

        c.execute(sql)  

        result = c.fetchone()  

        if result == None :  
            return None  

    return result[r"PASSWORD"]  

def get_user_name(self, user_cd=""):  
    '''  
    ユーザーコードからユーザ名を取得  
    :param user_cd:  
    :return:  
    '''  

    with closing(mysql.connector.connect(**self.config)) as conn:  

        c = conn.cursor(dictionary=True)  

        sql = "SELECT USER_NAME FROM MST_USER WHERE USER_CD = '" + user_cd + "'"  

        c.execute(sql)  

        result = c.fetchone()  

        if result == None :  
            return None  

    return result[r"USER_NAME"]

認証用クラス/メソッド追加


変更点
  1. RequestHandlerを継承した認証用のクラスを追加
  2. 認証処理を実施するクラスを追加
  3. ログアウト用のクラスを追加
  4. 「@tornado.web.authenticated」アノテーションでチェックをするように

Main.py

class AuthBaseHandler(tornado.web.RequestHandler):  
    '''  
    認証ハンドラー基底クラス  
    '''  
    cookie_user_cd = "user_cd"  

    def get_current_user(self):  
        logging.info("AuthBaseHandler [get_current_user]")  

        user_cd = self.get_secure_cookie(self.cookie_user_cd)  
        if not user_cd:  
            return ""  
        return user_cd.decode("UTF-8")  

    def set_current_user(self, user_cd):  
        logging.info("AuthBaseHandler [set_current_user]")  

        self.set_secure_cookie(self.cookie_user_cd, user_cd)  

    def clear_current_user(self):  
        logging.info("AuthBaseHandler [clear_current_user]")  

        self.clear_cookie(self.cookie_user_cd)  


class AuthLoginHandler(AuthBaseHandler):  
    '''  
    ログインハンドラー  
    '''  

    def get(self):  
        logging.info("AuthLoginHandler [get]")  

        self.render("Login.html", error_msg="")  

    def post(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 = False if input_password != password else True  

        if is_auth:  
            self.set_current_user(input_user_cd)  
            self.redirect("/main")  
        else:  
            self.render("Login.html", error_msg="ユーザーコードまたはパスワードが間違っています。")  

class AuthLogoutHandler(AuthBaseHandler):  

    def get(self):  
        self.clear_current_user()  
        self.redirect('/login')</pre>  
<h3>カレンダー取得処理の修正</h3>  
Main.py  
<pre class="lang:python decode:true ">    class GetCalendar(AuthBaseHandler):  
        """  
        カレンダー取得  
        """  

        def initialize(self):  
            logging.info("GetCalendar [initialize]")  

        @tornado.web.authenticated  
        def get(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

/**  
 * リクエスト送信.  
 */  
function sendAjaxRequest(method, eventData) {  

    var cal = $("#calendar").fullCalendar("getView");  
    eventData.searchStart = cal.start;  
    eventData.searchEnd = cal.end;  

    // 処理名を設定  
    var methodName = "登録";  
    if (method == "update") {  
        methodName = "更新"  
    } else if (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 にアップする予定です。

何かの参考になれば。

ではでは。

 

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@doraxdoraの技術ブログ 主に Java, C#, Python, Javascript の記事を載せていく予定。

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!