今日のPython 第26回「ログイン機能を追加してflask_sqlalchemyでユーザーをデータベース管理する」

2024年12月21日土曜日

Python_WEBアプリ編 公開

t f B! P L

 作成:2024/12/21

管理番号:W009


はじめに

今、PythonでWEBアプリの作成を進めています。少しづつ機能を追加して、ローカルの仮想環境で動作確認をするという作業を地道に行っている状況です。

そして、進捗がある都度、このブログで紹介しています。

前回までの状況

前回はjinja2のフィルタ機能を使用して、レコードの合計残高を表示してみました。

今、このような画面になっています。

■画面1■現在表示




改善が必要な点

今の状態では、誰でも家計簿を見ることが出来てしまいます。ローカルの仮想環境上ならよいですが、web上でこのwebアプリを使用する場合は、家計簿の内容を見ることが出来る人を限定できるようにしておかなければなりません。

今回の実施内容

そこで今回は、ログイン機能の追加にチャレンジです。

今、このプロジェクトに関連するファイルは、次のとおりで、◆が今回修正等を施すファイルです。

■関連ファイル■

Pythonファイル
◆main.pyサーバから最初に呼び出されるプログラム、バックエンドからテンプレートエンジンでテンプレートHTMLをレンダリングしてページを表示します。
◇KDpay.py収支データをクラスとして定義
HTMLファイル(テンプレートHTML)
◇index.htmlテスト用ページ
◆kd.html家計簿ページ
◇layout.html各ページの共通部分を抽出したレイアウトテンプレート
CSSファイル
◇index.cssページ装飾用、HTMLファイルから別ファイルとして独立させたもの
javascriptファイル
◇index.jsフロントエンドでjavascriptによりページに動きをつけるためのもの、HTMLファイルから別ファイルとして独立させたもの
json ファイル
◇KDjson.json

■■


では、始めます。


ログイン機能の追加

ログイン機能を追加するためにはFlask-loginというライブラリを使います。

Flaskでログイン機能を実装する際に使われることの多いライブラリだそうです。

複数のユーザーがログイン出来るようにしたいと思いますので、各ユーザーのIDやパスワード等を管理しなければなりません。

そこで今回は『SQLite』を使ってデータベースで管理しようと思います。

このデータベースの操作にはSQLAlchemyというライブラリを利用します。

SQLAlchemyを使用すると、SQLAlchemyがSQL文を生成してくれるため、SQLの文を書かずにデータベースを操作できるという、利点があります。

flaskからこのSQLAlchemyを利用する事ができるようにしたライブラリがFlask_SQLAlchemyです。

それでは、Flask-loginとflask_sqlalchemyでログイン機能を追加します。


ライブラリインストール

 まずは、先程紹介したライブラリをインストールします。
コンソールからpip でインストール します。
( 仮想環境の場合はpipenv)


$ pip install flask-login $ pip install flask_sqlalchemy



ライブラリのインポート

インストールしたライブラリをインポートします。

■「main.py」■

from flask import Flask, render_template, request, redirect, Response # --- 追加 9 redirect, Response
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required
from flask_sqlalchemy import SQLAlchemy  
from werkzeug.security import generate_password_hash, check_password_hash
#--- 追加 9 パスワードのハッシュ化


■■

一番最後のwerkzeug.securityはログイン時のパスワードをハッシュ化するためのものです。


データベースの作成

データベースを作成するにあたり、これまでのようにデータベースにアクセスして、既存のファイルが存在しなければ、新たに作成するというプログラムを組もうとしました。

ところが、そのようなプログラムが組めず、やむを得ず、コンソールからPythonモードに入って作成する方法で行うことにしました。

その手順を紹介します。

まず、VScodeのコンソールで仮想環境を有効化して


PS C:\Py\Project\MyPj> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process PS C:\Py\Project\MyPj> .\FlaskPj\Scripts\activate




Pythonモードに入ります。


(FlaskPj) PS C:\Py\Project\MyPj> python




そして、次のコマンドでデータベースを作成。


>>> from main import app >>> app.app_context().push() >>> from main import db >>> db.create_all()




私の環境では、このフォルダに作成されました。

C:\Py\Project\MyPj\FlaskPj\var\main-instance

ファイル名はuser_flask.dbです。



作業が終わったらpython モードを 終了します。


>>> exit()








データベースに接続

作成したデータベースに接続するためにメインプログラムである「main.py」に次のコードを追記します。
■「main.py」■


# DB設定 #--- 追加 9
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///user_flask.db'
app.config['SECRET_KEY'] = os.urandom(24) # パスワード設定 #--- 追加 9
db = SQLAlchemy(app) # DB設定 #--- 追加 9

login_manager = LoginManager()
login_manager.init_app(app)

# ユーザーDBの作成 #--- 追加 9
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(255), nullable=False, unique=True)
    password = db.Column(db.String(255))

# 認証ユーザーの呼び出し方を定義する  #--- 追加 9
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))



ログイン機能を追加するための定型句のようです。


新規登録画面の作成

新たに、ユーザー登録を行うためのページを新設します。

ファイル名:「signup.html」として作成。

■「signup.html」■


 <h1>ユーザ登録</h1>
  <form method="POST">
      <label for="">ユーザ名</label>
      <input type="text" name="username">
      <br>
      <label for="">パスワード</label>
      <input type="password" name="password">
      <br>
      <input type="submit" value="新規登録">
  </form>



登録したいユーザー名とパスワードを入力してPOST送信します。


新規登録画面で入力された内容を受け取った後の処理を「main.py」に追記します。

■「main.py」■


@app.route('/signup', methods=['GET', 'POST'])
def signup():
    if request.method == "POST":
        username = request.form.get('username')
        password = request.form.get('password')
        # Userのインスタンスを作成
        user = User(username=username, password=password)
        # hashを使用する場合(def login():も修正必要箇所あり)
        # user = User(username=username, password=generate_password_hash(password, method='pbkdf2:sha256'))        
        db.session.add(user)
        db.session.commit()
        return redirect('login')
    else:
        return render_template('signup.html')




受け取った情報をデータベースに追加して、ログイン画面に移ります。

  # hashを使用する場合

        # user = User(

の部分は、セキュリティを高めるためにパスワードをハッシュ化するコードですが、今はコメントアウトしておきます。


ログイン画面の作成

ユーザー登録が完了出来ると、ログイン画面に移ります。

そのために新たなページをファイル名:「login.html」として作成。


■「login.html」■


  <h1>ログイン</h1>
  <form method="POST">
      <label for="">ユーザ名</label>
      <input type="text" name="username">
      <br>
      <label for="">パスワード</label>
      <input type="password" name="password">
      <br>
      <input type="submit" value="ログイン">
  </form>




ログイン情報を送信した後の処理を
「main.py」に追記

■「main.py」■


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == "POST":
        username = request.form.get('username')
        password = request.form.get('password')
        # Userテーブルからusernameに一致するユーザを取得
        user = User.query.filter_by(username=username).first()
        if user.password == password:
        # hashを使用する場合(def signup()も修正必要箇所あり)
        # if check_password_hash(user.password, password):
            login_user(user)
            return redirect('/kakeibo') # 家計簿へ
        else:
            # 入力したユーザー名のパスワードが間違っている場合
            # return "<p>パスワードが間違っています。</p>"
            return Response(status=404, response="ページが見つかりません。")
       
    else:
        return render_template('login.html')




受け取った情報とデータベースに登録してある情報が一致していれば、家計簿ページに移ります。

ここでもハッシュ化のコードはコメントアウトしておきます。


ログアウトリンクの作成

家計簿ページにログアウト用のリンクも追加しておきます。

「kd.html」に次のように追記

■「kd.html」■


<a href="/logout" role=""button">ログアウト</a>







リンクがクリックされたときの処理を「main.py」に追記。

■「main.py」■


@app.route('/logout')
@login_required # ログインしているユーザーのみに制限 #--- 追加 9
def logout():
    logout_user()
    return redirect('login')





ログアウトしてログイン画面に移動します。

なお、

@login_required

は、ログインしているユーザーのみが処理出来るようにしたいときに使います。


以上で主な修正が一通り完了です。


エラーが発生

それではいよいよ実行です。

しかし
仮想環境で、メインプログラムを実行したところ、次のようなエラーが発生しました。



sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: user




どうやら、
データベースが見当たらないようです。

原因を究明する為に色々試していると、
先程の紹介したフォルダとは、別の場所に同じ名前のファイルが出力されているのを発見しました。

ここです。

C:\Py\Project\MyPj\instance




そして
C:\Py\Project\MyPj\FlaskPj\var\main-instance の user_flask.db ファイルを

C:\Py\Project\MyPj\instance の user_flask.db ファイルに上書きし、
メインプログラムを実行するとうまくいきました。

これが、正しい処理方法なのか、設定に不備があって2箇所にファイルが作成されたのか、分からないのですが、とりあえず動いたので、原因究明は今後の課題とします。


実施結果

画面表示

では、ブラウザで表示してみます。

仮想環境ですので

http://127.0.0.1:5000/signup


として表示すると、このように表示されます。



新規登録画面


ユーザー登録後のログイン画面

ログイン後の家計簿画面

しっかり登録、ログインが出来ました。
ログアウトリンクをクリックするとログアウトしてログイン画面に戻ります。

現在のフォルダとファイルの構成

現在のフォルダとファイルの構成は、下図のようになります。




C:\Py\Project\MyPj\
                 |--FlaskPj\
                 |   |--templates\
                 |   |    |--kakeibo_test_json\ 
                 |   |    |    └--kd.html 2024/12/21★修正
                 |   |    |--layout.html 2024/12/9 
                 |   |    |--index.html 2024/12/7 
                 |   |    |--signup.html 2024/12/21★追加
                 |   |    └--login.html 2024/12/21★追加
                 |   |--static\
                 |   |    |--javascript\ 
                 |   |    |    └--index.js 2024/12/15
                 |   |    └--index.css 2024/12/3
                 |   |--var\ ★追加
                 |   |    └--main-instance\ 
                 |   |        └--user_flask.db 2024/12/21★追加
                 |   |  
                 |   |--KDpay.py 2024/10/12
                 |   └--main.py 2024/12/21★修正
                 |
                 |--instance\ ★追加
                 |  └--user_flask.db 2024/12/21★追加
                 └--KDjson.json 2024/12/8





ソースコード

本文中は、変更点のみを記載しています。

全コードはこちらです。(→参照


おわりに

以上、今回は、ログイン機能の追加にチャレンジしてみました。

一応形になって、動きましたが、まだ理解不足なところも多々あります。

経験を積みながら、少しずつ理解を深めていきたいと思います。




このブログを検索

アーカイブ

カテゴリー

QooQ