Visual StudioとFlaskで作る書籍管理アプリ:ログイン機能の実装

この記事では、Flaskにおける基本的なログイン処理を実装します。
コード全体の概要は『Visual StudioとFlaskで作る書籍管理アプリ:外枠を作る』を参照してください。

ログイン機能は主にFlaskの公式チュートリアルを参考にしています。
ただし、Visual StudioのFlaskプロジェクトの使用を前提としているため、フォルダの構成や画面のデザインは変わっています。処理フローも読みやすくなるように書き替えました。
また、Bootstrapを使って、比較的簡単に、それなりにきれいな画面を作っていきます。

この記事はVisual StudioとFlaskで書籍管理アプリを作ってみたという一連の記事の中の1つです。
詳しくは以下の記事も参照してください
Visual Studioで作ってみたFlaskアプリ
Visual StudioとFlaskで作る書籍管理アプリ:概要

この記事ではWebアプリの開発を何も知らない人を対象とします。といっても、この記事の著者もWebアプリ開発に明るくありません。自分の勉強の意味も込めて書きました。
誤りが含まれるかもしれませんが、ご容赦ください。何かあれば、ご指摘いただけますと幸いです。



スポンサードリンク

目次

  1. 新規ユーザー登録処理の実装
  2. ログイン処理の実装
  3. ログアウト処理の実装
  4. ユーザー情報の取得
  5. ユーザー登録とログイン画面の実装
  6. 動作確認

 

1.新規ユーザー登録処理の実装

新規ユーザー登録処理から実装していきます。
コード全体の概要は『Visual StudioとFlaskで作る書籍管理アプリ:外枠を作る』を参照してください。

処理の流れを簡単にフローチャートとしてみました。
フローチャートは『draw.io』を使わせてもらいました。

 
実装すると、こうなります。

@bp.route('/create_user', methods=('GET', 'POST'))
def create_user():
    """
    GET :ユーザー登録画面に遷移
    POST:ユーザー登録処理を実施
    """
    if request.method == 'GET':
        # ユーザー登録画面に遷移
        return render_template('auth/create_user.html',
                               title='ユーザー登録',
                               year=datetime.now().year)


    # ユーザー登録処理

    # 登録フォームから送られてきた、ユーザー名とパスワードを取得
    username = request.form['username']
    password = request.form['password']

    # DBと接続
    db = get_db()

    # エラーチェック
    error_message = None

    if not username:
        error_message = 'ユーザー名の入力は必須です'
    elif not password:
        error_message = 'パスワードの入力は必須です'
    elif db.execute('SELECT id FROM user WHERE username = ?', (username,)).fetchone() is not None:
        error_message = 'ユーザー名 {} はすでに使用されています'.format(username)


    if error_message is not None:
        # エラーがあれば、それを画面に表示させる
        flash(error_message, category='alert alert-danger')
        return redirect(url_for('auth.create_user'))


    # エラーがなければテーブルに登録する
    # パスワードはハッシュ化したものを登録
    db.execute(
        'INSERT INTO user (username, password) VALUES (?, ?)',
        (username, generate_password_hash(password))
    )
    db.commit()

    # ログイン画面へ遷移
    flash('ユーザー登録が完了しました。登録した内容でログインしてください', category='alert alert-info')
    return redirect(url_for('auth.login'))

処理の内容はすべてコメントに入れてあります。

ところで、パスワードを保存する際、その内容は他の人に見られたくないですね。そこで44行目のように『generate_password_hash(password)』とすることでハッシュ化しています。

例えば36行目などで『flash(error_message, category=’alert alert-danger’)』としてメッセージを次の画面に渡しています。
エラーメッセージの場合はcategoryを『alert alert-danger』として、単に情報を伝える場合は51行目のように『alert alert-info』とします。これで画面に表示されるメッセージの色が変わるようにしています。
メッセージのcategoryに合わせて文字の色を変える方法は、テンプレートの実装においても説明します。

 

2.ログイン処理の実装

ログイン処理は、処理の流れとしてはほとんど「ユーザー登録処理」と同じですので、フローチャートは省略します。
コードを一気に載せます。

@bp.route('/login', methods=('GET', 'POST'))
def login():
    """
    GET :ログイン画面に遷移
    POST:ログイン処理を実施
    """
    if request.method == 'GET':
        # ログイン画面に遷移
        return render_template('auth/login.html',
                               title='ログイン',
                               year=datetime.now().year)


    # ログイン処理

    # ログインフォームから送られてきた、ユーザー名とパスワードを取得
    username = request.form['username']
    password = request.form['password']

    # DBと接続
    db = get_db()

    # ユーザー名とパスワードのチェック
    error_message = None

    user = db.execute(
        'SELECT * FROM user WHERE username = ?', (username,)
    ).fetchone()

    if user is None:
        error_message = 'ユーザー名が正しくありません'
    elif not check_password_hash(user['password'], password):
        error_message = 'パスワードが正しくありません'

    if error_message is not None:
        # エラーがあればそれを表示したうえでログイン画面に遷移
        flash(error_message, category='alert alert-danger')
        return redirect(url_for('auth.login'))


    # エラーがなければ、セッションにユーザーIDを追加してインデックスページへ遷移
    session.clear()
    session['user_id'] = user['id']
    flash('{}さんとしてログインしました'.format(username), category='alert alert-info')
    return redirect(url_for('home'))

パスワードはハッシュ化されているため、32行目のように『check_password_hash』という専用の関数を使ってパスワードのチェックをしています。

ログインの処理としては、『session.clear()』としてセッションをクリアした後、新たにユーザーIDをセッションに追加しました。



スポンサードリンク

 

3.ログアウト処理の実装

ログアウト処理は、セッションを空にするだけなので簡単です。
以下にコードを載せます。

@bp.route('/logout')
def logout():
    """ログアウトする"""
    session.clear()
    flash('ログアウトしました', category='alert alert-info')
    return redirect(url_for('home'))

 

4.ユーザー情報の取得

続いて「どのURLが要求されても、ビュー関数の前で実行される関数」を実装します。これは頭に「@bp.before_app_request」とすれば達成できるのでした。

セッションからユーザーIDを取得したうえで、データベースにアクセスして、ユーザー情報を取得します。

@bp.before_app_request
def load_logged_in_user():
    """
    どのURLが要求されても、ビュー関数の前で実行される関数
    ログインしているか確認し、ログインされていればユーザー情報を取得する
    """
    user_id = session.get('user_id')

    if user_id is None:
        g.user = None
    else:
        db = get_db()
        g.user = db.execute(
            'SELECT * FROM user WHERE id = ?', (user_id,)
        ).fetchone()

ログインを要請する関数「login_required」は『Visual StudioとFlaskで作る書籍管理アプリ:外枠を作る』で実装済みなので省略します。

 

5.ユーザー登録とログイン画面の実装

次は画面を作ります。チュートリアルを参考にして、templatesフォルダの中にさらにauthフォルダを作り、その中にテンプレートファイルを追加していくことにします。

Visual Studioのソリューションエクスプローラーで、templatesフォルダを右クリックします。そして『追加』→『新しいフォルダ』として「auth」フォルダを作ります。

「auth」フォルダを右クリックして『追加』→『新しい項目』→『HTMLページ』として、「create_user.html」と「login.html」を作ります。

 
create_user.htmlは以下のようになります。

{% extends "layout.html" %}

{% block content %}

    <h1 style="margin-bottom:50px">ユーザー登録画面</h1>

    {% for category, message in get_flashed_messages(with_categories=true) %}
    <div class="{{ category }}">{{ message }}</div>
    {% endfor %}

    <form method="post">
        <div class="form-group">
            <label>ユーザー名</label>
            <input type="text" class="form-control" name="username">
        </div>
        <div class="form-group">
            <label>パスワード</label>
            <input type="password" class="form-control" name="password">
        </div>
        <button type="submit" class="btn btn-primary">登録</button>
    </form>

{% endblock %}

7~9行目は、「flash」で送られてきたメッセージの表示です。
エラーがあればここに赤い文字で表示されます。メッセージがなければ何も表示されません。

11行目からが登録Formの実装となります。
Bootstrapを使っているため、例えば12行目で『class=”form-group”』などとすることで、簡単に見栄えをよくすることができます。
20行目で登録ボタンのclassを『btn btn-primary』にしてありますが、お好きな色に変更してもらってもいいと思います。
この辺の修正は外部サイトですが『Bootstrapに用意されているクラス【ボタン編】|Webお役立ちネタ帳』を参照してください。簡単に見た目を変えられるので、楽しいです。

 

ログイン画面は、ページ見出し以外はほとんど同じ実装で行けます。
login.htmlを載せます。

{% extends "layout.html" %}

{% block content %}

    <h1 style="margin-bottom:50px">ログイン画面</h1>

    {% for category, message in get_flashed_messages(with_categories=true) %}
    <div class="{{ category }}">{{ message }}</div>
    {% endfor %}

    <form method="post">
        <div class="form-group">
            <label>ユーザー名</label>
            <input type="text" class="form-control" name="username">
        </div>
        <div class="form-group">
            <label>パスワード</label>
            <input type="password" class="form-control" name="password">
        </div>
        <button type="submit" class="btn btn-primary">ログインする</button>
    </form>

{% endblock %}


 

6.動作確認

ここまでが全部実装できれば、とりあえずログイン処理までは動きます。
F5デバッグすると、画面が立ち上がるはずです。

 
まずはナビゲーションバーから、ユーザーの新規登録画面を表示させます。

 
ユーザー名を入れないで登録しようとすると、エラーになります。

 
例えば、ユーザー名を「test」パスワードを「pass」として登録をすると、正しく処理されて、ログイン画面へ遷移します。

 
ここでDB Browser for SQLite等を使ってuserテーブルの中身を除くと、パスワードがハッシュ化されてわからなくなっていることが確認できます。

 
登録された情報でログインすると、トップページに遷移します。
ログインに成功した旨のメッセージが表示されます。

 
ナビゲーションバーのリンクをクリックして、ログアウトします。

 
ログアウトしている状態で、『書籍一覧へ』ボタンを押すと、ログインをするようにワーニングが出ます。

本当はエラーのパターンなど全部を網羅して確認する必要があるのですが、今回はテストを省略します。

 

次は『Visual StudioとFlaskで作る書籍管理アプリ:書籍管理機能の実装』に進みます。

 

関連する記事

 

参考文献

Welcome to Flask
→Flaskの全体像をつかむことができる公式サイトです。ここのチュートリアルを参考にしました。


独学プログラマー Python言語の基本から仕事のやり方まで

 
Pythonのことがさっぱりわからないという方は、この本から入ると良いかと思います。あまり分厚くない本ですので、読み切ることは難しくないはずです。
Pythonの基本文法やコードを書くコツなどが載っています。
 

入門Python3

 
こちらは分厚い本ですが、その分細かく載っています。Flaskの解説も少しあります。辞書的な使い方をしても良い本かと思います。
 

演習で力がつく HTML/CSSコーディングの教科書

 
HTMLとCSSはこの本を読んで勉強しました。
Web開発の書籍はたくさんあるので、お好きなものを選ばれたら良いかなと思います。
 



スポンサードリンク

 
更新履歴
2018年5月26日:新規作成

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください