見出し画像

Pythonでステップバイステップ!Flaskによるブログ作成シリーズ第2弾

この記事は、Python と Flask を使用してブログを作成するシリーズの第2回目に当たります。

2024/4/12 次の2つの章を追加しました 

  1. 『ユーザ定義及び、データベース変更に伴う改修点』

  2. 『ログインテスト前に」

前回の内容を踏まえて進める予定でしたが、仕様の一部を変更したため、その変更点について先にご説明します。


仕様の変更点

仕様の変更は主に2点に集約されます。

  • ユーザモデル定義の変更

  • ログイン方法の変更

それでは、これらの変更点について詳しく見ていきましょう。

ユーザモデル定義の変更

変更前の定義 :

# ユーザモデル定義 
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(100), unique=True, nullable=False) # ユーザ名 
    password = db.Column(db.String(100), nullable=False)  # パスワード

変更後の定義 :

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(100), unique=True, nullable=False)
    email = db.Column(db.String(100), unique=True, nullable=False)  # メールアドレスの追加
    password = db.Column(db.String(100), nullable=False)
    name = db.Column(db.String(15), nullable=False)  # 投稿者名の追加
    role = db.Column(db.String(10), nullable=False)  # 役割の追加(admin か poster)

ログイン方法の変更

従来はユーザIDを用いてログインする予定でしたが、今回からメールアドレスを利用する新しい仕様に変更しました。
この変更は、ユーザー認証プロセスをより安全に、かつ使いやすくすることを目的としています。

仕様変更に伴う注意点

仕様変更により、データベースのテーブル構造にも変更が生じ、新たに3つのカラムが追加されました。
これに伴い、プログラムの改修だけでなく、データベースのテーブルも適宜更新する必要があります。最も簡単な方法は、テーブルを一度削除し、改修後のプログラムを再実行してテーブルを再構築することです。

テーブルの再構築

まだ、初期の段階ですので、テーブルを削除して新しくテーブルを作り直した方が簡単です。
テーブルを削除した後、改修されたプログラムを再実行すれば適応されたテーブルが作り直されます。

SQLite データベースに接続

まず、ターミナルから SQLite3 にアクセスします。

sqlite3 .\instance\yourdatabase.db

user テーブルの確認

.table

不要なテーブルの削除

DROP TABLE IF EXISTS user;

SQLite のコマンドラインツール終了

.exit または .quit

ここまで終わって、プログラムを実行しエラーが発生せず、ログイン画面が表示されれば無事にプログラム及び、データベーステーブルが正しく変更されています。

データベースへの接続やテーブルの削除、新規作成方法については、具体的なコマンドを示しながら説明しました。
これらのプロセスを正確に実行することで、プログラムとデータベースのテーブルが正しく変更され、エラーなくログイン画面が表示されることが確認できます。

テーブルの新規作成(参考資料)

上記方法でプログラムを改修した後、プログラムを実行してエラーが発生していなければこの内容は飛ばして結構です。
下記はテーブルを削除した後にプログラムを実行せずに、テーブルを新しく作り直す手動の方法です。

新しい user テーブルの作成

このコマンドで新しく user テーブルが生成されます。

CREATE TABLE user (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username TEXT NOT NULL UNIQUE,
    email TEXT NOT NULL UNIQUE,
    password TEXT NOT NULL,
    name TEXT NOT NULL,
    role TEXT NOT NULL
);

生成された user テーブルのカラム確認

次のコマンドで確認できます。

PRAGMA table_info(user);

SQLite のコマンドラインツール終了

.exit または .quit

参考までに user テーブルの中身を確認する場合は次のようにします。

SELECT * FROM user;

この章では、プログラムを改修してから新しいテーブルを手動で作成する方法について詳しく解説しました。まず、既存の user テーブルを削除した後、プログラムを再実行するのではなく、新しい user テーブルを手動で作成する手順を紹介しました。新しいテーブルは CREATE TABLE コマンドを使用して作成され、id, username, email, password, name, role といったカラムが含まれます。

テーブルが正しく作成されたかどうかは、PRAGMA table_info(user); コマンドで確認することができ、これにより各カラムの詳細情報を確認できます。最後に、SQLite のコマンドラインツールから .exit または .quit コマンドで正常に退出します。

さらに、テーブルに既にデータが入力されている場合の確認方法として、SELECT * FROM user; コマンドを用いて user テーブルの中身を確認する方法も紹介しました。これにより、テーブルの新規作成後にデータが正しく格納されているかどうかを確認することが可能です。

全体を通して、この章ではデータベースのテーブルを手動で新規作成する際の具体的なコマンドとその実行方法に焦点を当て、参考資料としての役割を果たしています。プログラムの改修後にテーブルの更新が必要な場合に、この方法を参照することで、スムーズにデータベースを管理することができるようになります。

ユーザ定義及び、データベース変更に伴う改修点

コードの変更点

# ログインページ
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        email = request.form['email']  # ユーザー名からメールアドレスへ変更
        password = request.form['password']
        user = User.query.filter_by(email=email).first()  # username を email に変更
        
        if user and check_password_hash(user.password, password):
            login_user(user)
            return redirect(url_for('dashboard'))
        else:
            return 'Login Failed'
    return render_template('login.html')

HTMLの変更点

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
</head>
<body>
    <form action="" method="post">
        Email: <input type="text" name="email"><br>  #username から emailへ
        Password: <input type="password" name="password"><br>
        <input type="submit" value="Login">
    </form>
</body>
</html>

今回の予定

  • ユーザ登録の機能を実装をします。

  • 見た目がしょぼいので Bootstrap で CSS 周りを簡単に表現を整えます。

  • 実際にログインテストを行い、'dashboard' が表示されるところまで進めます。

ユーザ登録機能の実装とテスト

register.html をテンプレートとして、ユーザ定義に基づきユーザを登録するためのコードは以下の通りになります。

@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.form['username']
        email = request.form['email']
        password = request.form['password']
        name = request.form['name']
        role = request.form['role']

        hashed_password = generate_password_hash(password, method='pbkdf2:sha256')
        new_user = User(email=email, username=username, password=hashed_password, name=name, role=role)
        db.session.add(new_user)
        db.session.commit()

        return redirect(url_for('login'))
    return render_template('register.html')

詳しいコード説明は次に続きます。

この章では、ユーザー登録機能の実装に必要なコードを紹介しました。
具体的には、register.html テンプレートを用いて、フォームから送信されたユーザー情報(ユーザーネーム、メールアドレス、パスワード、名前、役割)をデータベースに登録するプロセスを説明しています。
このプロセスでは、ユーザーから送信されたパスワードを安全に保管するために、pbkdf2:sha256 アルゴリズムを使用してハッシュ化し、その後、新しいユーザー情報をデータベースに追加しています。
また、ユーザーの登録が完了した後は、ログインページへリダイレクトされる流れも含まれています。

ユーザ登録コードの解説

■ register の呼び出しと定義

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

/register にアクセスがあった場合に、関連付けた関数(register)を呼び出しています。
def register(): は関数の定義をしている。


■ post メソッドだったらユーザ登録を行う

if request.method == 'POST'

リクエストが POST メソッドであれば、つまり、ユーザがフォームを送信した場合にのみに「以下のコードが実行されます。

■ 各フォームから各値を取得し、各変数に登録する

        username = request.form['username']
        email = request.form['email']
        password = request.form['password']
        name = request.form['name']
        role = request.form['role']

例)
username = request.form['username'] の場合
equest.form['username'] (フォーム)から取得した値を、username に格納しています。

■ パスワードを安全に保存するために、ハッシュ化(暗号化)

hashed_password = generate_password_hash(password, method='pbkdf2:sha256')

この行では、パスワードを安全に保管するために、pbkdf2:sha256 アルゴリズムを使用してハッシュ化しています。

■ ユーザ情報のモデルインスタンスを作成、データベースセッションの追加とコミット

new_user = User(email=email, username=username, password=hashed_password, name=name, role=role)
db.session.add(new_user)
db.session.commit()

ここでは上記 3 行のコードを詳しく説明します。

  1. 「ユーザー情報を使用して新しい User オブジェクト(モデルインスタンス)を作成します。」このプロセスでは、プログラム内で新しいユーザーの情報をまとめて、それをデータベースに保存できる形に準備します。

  2. 「新しく作成されたユーザーオブジェクトをデータベースセッションに追加します。」ここでいう「データベースセッションに追加」とは、実際には新しいユーザーのデータをデータベースに送る前の準備段階です。この時点では、データはまだデータベースには保存されていません。このプロセスを「挿入の予約」と考えることができます。簡単に言うと、「このデータを後でデータベースに追加するよ」という予定を立てている状態です。

  3. 「セッションに追加された「挿入」の予約をデータベースにコミット(確定)します。」この段階で初めて、予約していたデータの挿入が実行され、データベースに保存されます。この「コミット」という操作を行うことで、先ほど予約したデータが実際にデータベースに追加されるわけです。つまり、この操作によって、新しいユーザーの情報がデータベースに正式に保存されるというわけです。

■ ユーザ登録完了後のリダイレクト先設定

return redirect(url_for('login'))

ここではログインページへリダイレクトします。
url_for('login') は login関数に対応する URL を生成します。

ここまでが if request.method == 'POST': のブロックでした。

■ POST リクエストが無い場合の処理

return render_template('register.html')

POSTリクエストがない場合(つまり、ユーザーがフォームを送信していない場合)、ユーザー登録フォームを含むHTMLページ(register.html)を表示します。

この章では、ユーザー登録機能の具体的な実装方法について詳しく解説しました。まず、@app.route('/register', methods=['GET', 'POST']) を使用して、/register へのアクセスがあった際に実行される register 関数を定義しました。この関数内で、リクエストのメソッドが POST である場合にのみ、フォームから送信されたユーザー情報をデータベースに登録する処理が行われます。具体的には、username, email, password, name, role の各値を取得し、passwordpbkdf2:sha256 アルゴリズムを使用してハッシュ化した後、新しい User オブジェクトを作成してデータベースセッションに追加し、コミットします。これにより、新しいユーザー情報がデータベースに正式に保存されます。

また、ユーザー登録が完了した後は、login 関数に対応する URL へリダイレクトされるように設定されています。一方で、リクエストが GET メソッドの場合や、POST メソッドであってもフォームが送信されていない場合は、ユーザー登録フォームを表示する register.html がレンダリングされます。

register.html のひな形

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Register</title>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <h2 class="mt-5">ユーザー登録</h2>
        <form action="/register" method="post">
            <div class="form-group">
                <label for="username">ユーザ名</label>
                <input type="text" class="form-control" id="username" name="username" required>
            </div>
            <div class="form-group">
                <label for="name">投稿者名</label>
                <input type="text" class="form-control" id="name" name="name" required>
            </div>
            <div class="form-group">
                <label for="role">権限</label>
                <select class="form-control" id="role" name="role" required>
                    <option value="admin">管理者</option>
                    <option value="poster">投稿者</option>
                </select>
            </div>            
            <div class="form-group">
                <label for="email">メールアドレス</label>
                <input type="email" class="form-control" id="email" name="email" required>
            </div>
            <div class="form-group">
                <label for="password">パスワード</label>
                <input type="password" class="form-control" id="password" name="password" required>
            </div>
            <button type="submit" class="btn btn-primary">登録</button>
        </form>
    </div>
</body>
</html>

フォームの見た目を整えるために、簡単に Bootstrap CSS へのリンクを入れてあります。

フォームには、ユーザ名、投稿者名、権限(管理者または投稿者を選択)、メールアドレス、パスワードの入力フィールドが含めてあり、すべてのフィールドが入力必須であることが required 属性によって指定しています。
最後に、「登録」ボタンが設置されており、このボタンをクリックすることで登録処理が実行されます。

ユーザー登録機能の実装では、register.html をテンプレートとして使用し、フォームから送信されたデータをデータベースに保存する処理を紹介しました。また、登録フォームの見た目を整えるために Bootstrap を用いる方法も示しました。

ユーザ登録テスト

プログラムを実行し、次の URL にアクセスしてください。

http://127.0.0.1:5000/register

次のようにフォームが表示されます。

http://127.0.0.1:5000/register にアクセス

フォームに値を入力し、' 登録 ' をクリックすると、login.html が表示されます。

ここでエラーが発生した場合は、エラー内容を調べて対応してください。

SQLite3 でデータベースの内容をチェック

ターミナルでデータベースに接続して、内容が正しく登録されていることを確認します。

SQLite3 へ接続

sqlite3 .\instance\yourdatabase.db

テーブル user の情報を確認

SELECT * FROM user;

SELECT文を実行すると次のような結果が返ってきてれば成功です。

1|miyakawa|miyakawa@hoge.com|pbkdf2:sha256:6000・・・中略・・・f6d5d3c60e|testUser|poster

この章では、ユーザー登録機能のテスト方法について説明しました。まず、プログラムを実行し、ブラウザから http://127.0.0.1:5000/register にアクセスすることで、ユーザー登録フォームが正しく表示されるかを確認します。フォームに必要な情報を入力し、「登録」ボタンをクリックすると、login.html が表示される流れをテストします。このプロセス中にエラーが発生した場合は、エラーの内容を確認し、適切に対応する必要があります。

さらに、ユーザー登録機能のテストプロセスについても説明し、登録したユーザー情報が正しくデータベースに保存されているかを確認する方法についても触れました。

ログインテスト前に

現在のコードだとメールアドレスまたは、パスワードが間違っていても、「LoginFiled」と表示されるだけなので、エラーメッセージを表示できるようにしたいと思います。

コードの調整---Flask フラッシュメッセージの利用

Flask にはフラッシュメッセージング機能があり、これを使ってユーザーに一時的なメッセージを表示することができます。これを利用してエラーメッセージを表示します。

まず、flask モジュールから flash をインポートしてください。

from flask import Flask, render_template, request, redirect, url_for, flash

次に、ログイン関数内でエラーメッセージをフラッシュするように修正します。

# ログインページ
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        email = request.form['email']
        password = request.form['password']
        user = User.query.filter_by(email=email).first()
        
        if user and check_password_hash(user.password, password):
            login_user(user)
            return redirect(url_for('dashboard'))
        else:
            flash('メールアドレスまたはパスワードが違います')  # エラーメッセージをフラッシュ
            return redirect(url_for('login'))
    return render_template('login.html')

テンプレートでのフラッシュメッセージの表示

register.html で Bootstrap を利用して見やすくしましたので、login.html も見た目を調整しています。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <h2 class="mt-5">ログイン</h2>
        {% if get_flashed_messages() %}
            {% for message in get_flashed_messages() %}
            <div class="alert alert-warning" role="alert">
                {{ message }}
            </div>
            {% endfor %}
        {% endif %}
        <form action="/login" method="post">
            <div class="form-group">
                <label for="email">メールアドレス</label>
                <input type="email" class="form-control" id="email" name="email" required>
            </div>
            <div class="form-group">
                <label for="password">パスワード</label>
                <input type="password" class="form-control" id="password" name="password" required>
            </div>
            <button type="submit" class="btn btn-primary">ログイン</button>
        </form>
    </div>
</body>
</html>

以上の調整が終わったら次にログインテストです。

いよいよログインテスト

引き続いてログインテストを行います。
ログインするためには先ほどユーザ登録したユーザのメールアドレスとパスワードを使います。

http://127.0.0.1:5000/ にアクセスして、実際に登録したユーザでログインできるかテストを行います。

  • ログイン成功

    • ' Welcome to the Dashboard' とブラウザに表示

  • メールアドレス、パスワードが間違っている場合

    • 'メールアドレスまたはパスワードが違います' が表示

最後に、ユーザー登録が成功した後に行うログインテストについても解説し、登録した情報を用いたログインが正常に行われることを確認しました。

まとめ

 この記事を通じて、私たちは Python と Flask を駆使してブログを作成する旅の第2段階を歩みました。途中、ユーザーモデルの拡張とログイン方法の改良という、2 つの大きな変更に直面しました。これらの変更を一緒に掘り下げることで、より安全で使いやすいブログシステムの基礎を固めることができました。

仕様の変更に伴うデータベースの更新作業から始まり、新しいユーザー登録機能の実装、そして変更を反映したテストの実施まで、一連のプロセスを詳しく解説しました。これらの手順は、ブログシステムの開発において不可欠な作業であり、セキュリティとユーザーエクスペリエンスの向上に寄与します。

改良されたユーザーモデルにより、より詳細なユーザー情報の管理が可能となり、新しいログイン方法は認証プロセスの安全性を一層高めています。このようにして、ブログシステムは次のステップへと進む準備が整いました。読者の皆様がこれらの変更を自身のプロジェクトに応用し、開発の幅を広げていくことを願っています。

このシリーズを通じて、ブログ作成の道のりは単にコーディング技術を磨く旅ではなく、より良いユーザーエクスペリエンスを提供するための洞察を深める機会でもあります。次回の記事で、この冒険がどのように続くのか、楽しみにしていてください。



この記事が気に入ったらサポートをしてみませんか?