ビジネスパーソン・ガジェット置場 empty lot for business

営業や仕事、それに伴う生活を便利に楽にするツール、ガジェットを作ります。既にあるツールも自分用にカスタマイズ。

python: Flaskアプリの作成17 ログイン

備忘録です。

今回は、ログイン機能を実装します。

ログイン

Flask-Loginでログイン機能を実装

 

その1 pipでライブラリをインストール

$ pip3 install flask-login

 

ここまででインストールしたライブラリは下記のようになってます。

alembic==1.8.1
click==8.1.3
DateTime==4.5
Flask==2.2.1
Flask-CKEditor==0.4.6
Flask-Login==0.6.2
Flask-Migrate==3.1.0
Flask-SQLAlchemy==2.5.1
Flask-WTF==1.0.1
greenlet==1.1.2
importlib-metadata==4.12.0
itsdangerous==2.1.2
Jinja2==3.1.2
Mako==1.2.1
MarkupSafe==2.1.1
pytz==2022.1
SQLAlchemy==1.4.39
Werkzeug==2.2.1
WTForms==3.0.1
zipp==3.8.1
zope.interface==5.4.0

 

その2 ライブラリをインポート

 

# app.py

from flask_login import UserMixin, login_user, LoginManager, login_required, logout_user, current_user

 

その3 ユーザーテーブルにUserMixinとハンドルネームのカラムを追加

# app.py

# ユーザー用モデル
class Users(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    handlname = db.Column(db.String(20), nullable=False, unique=True)
    name = db.Column(db.String(255), nullable=False)
    email = db.Column(db.String(120), nullable=False, unique=True)
    u_created_at = db.Column(db.DateTime, default=datetime.now(pytz.timezone('Asia/Tokyo')))
    password_hash = db.Column(db.String(128))

UserMixin・・・ログインしたユーザーの情報を管理する

 

ターミナルでDBにプッシュ

$ flask db migrate -m 'added handlename'
$ flask db upgrade

 

その4 フォームにhandlnameを追加し登録できるように実装

# add_user.html

  <form method="POST">
    {{ form.hidden_tag() }}

    {{ form.name.label(class="form-label") }}
    {{ form.name(class="form-control") }}
    <br/>
    # ハンドルネームを投稿する項目を追加
    {{ form.handlname.label(class="form-label") }}
    {{ form.handlname(class="form-control") }}
    <br/>

    {{ form.email.label(class="form-label") }}
    {{ form.email(class="form-control") }}
    <br/>

    {{ form.password_hash.label(class="form-label") }}
    {{ form.password_hash(class="form-control") }}
    <br/>

    {{ form.password_hash2.label(class="form-label") }}
    {{ form.password_hash2(class="form-control") }}
    <br/>

# app.py

# ユーザーを登録するページ
@app.route('/user/add', methods=['GET', 'POST'])
def add_user():
    name = None
    form = UserForm()
    if form.validate_on_submit():
        # ユニークチェック
        user = Users.query.filter_by(email=form.email.data).first()
        if user is None:
            hashed_pw = generate_password_hash(form.password_hash.data, "sha256")
            # ハンドルネームの項目を追加
            user = Users(name=form.name.data, handlname=form.handlname.data, email=form.email.data, password_hash=hashed_pw)
            db.session.add(user)
            db.session.commit()
        name = form.name.data
        form.name.data = ''
        # ハンドルネームの項目を追加
        form.handlname.data = ''
        form.email.data = ''
        form.password_hash.data = ''
    our_users = Users.query.order_by(Users.u_created_at)
    return render_template('add_user.html', form=form, name=name, our_users=our_users)

 上記3ヶ所にハンドルネームの項目を追加

 

その5 全ユーザー表示ページで表示し追加されたかチェック

# all_user.html

{% extends "base.html"%}

{% block content %}
<br/>
<h1>全ユーザー</h1>
  <br/><br/><br/>
  <table class="table">
    <thead>
      <tr>
        <th scope="col">#</th>
        <th scope="col">name</th>
        #ハンドルネームの項目を追加
        <th scope="col">handlname</th>
        <th scope="col">email</th>
        <th scope="col">操作</th>
      </tr>
    </thead>
    <tbody>
      {% for our_user in our_users %}
      <tr>
        <th scope="row">{{ our_user.id }}</th>
        <td>{{ our_user.name }}</td>
        # ハンドルネームの項目を追加
        <td>{{ our_user.handlname }}</td>
        <td>{{ our_user.email }}</td>
        <td><a href="{{ url_for('edit_user', id=our_user.id) }}" class="btn btn-outline-secondary btn-sm">編集</a>
        <a href="{{ url_for('delete_user', id=our_user.id) }}" class="btn btn-outline-danger btn-sm">削除</a></td>
      </tr>
      {% endfor %}
    </tbody>
  </table>

{% endblock %}

 

その6 ログインフォームとログイン後の遷移ページを作成

# login.html

{% extends "base.html"%}

{% block content %}
<br/>
<h1>ログインフォーム</h1>
<br/>
<div class="shadow p-3 mb-5 bg-body rounded">
  <form method="POST">
    {{ form.hidden_tag() }}

    {{ form.handlname.label(class="form-label") }}
    {{ form.handlname(class="form-control") }}
    <br/>

    {{ form.password.label(class="form-label") }}
    {{ form.password(class="form-control") }}
    <br/>

    {{ form.submit(class="btn btn-secondary") }}

  </form>
  <br/><br/><br/>

</div>

ハンドルネームとパスワードでログインさせます。

# dashboard.html

{% extends "base.html"%}

{% block content %}
<br/>
<h1>ダッシュボード</h1>
<br/>
<p>ログイン</p>


{% endblock %}

ログイン後の遷移ページです。

 

その7 ログインフォームの作成

# app.py

# ログインフォーム
class LoginForm(FlaskForm):
    handlname = StringField('Handlname', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    submit = SubmitField('Submit')

その8 ログインマネージャーの実装

# app.py

# ログインマネージャーの実装
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

@login_manager.user_loader
def load_user(user_id):
    return Users.query.get(int(user_id))

 

その9 app.pyでログインページとダッシュボードページを作成

# app.py

# ログインページの作成
@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        # ハンドルネームでフィルターをかけデータを見つける
        user = Users.query.filter_by(handlname=form.handlname.data).first()
        # ユーザーが見つかった場合
        if user:
            # ハッシュをチェックしあっていれば
            if check_password_hash(user.password_hash, form.password.data):
                # ユーザーを返す関数を実行
                login_user(user)
                return redirect(url_for('dashboard'))
            # パスワードが違う場合
            else:
                flash("パスワードが違います。パスワードを確かめてください")
        # ユーザーが見つからない場合
        else:
            flash('ユーザーが存在しません')
    return render_template('login.html', form=form)

# ダッシュボードページの作成
@app.route('/dashboard', methods=['GET', 'POST'])
def dashboard():

    return render_template('dashboard.html')

ログインページは条件付けでハッシュのチェックなどを実装しています。

 

その10 ログインが必要なページにlogin_requiredを実装

#app.py

# ダッシュボードページの作成
@app.route('/dashboard', methods=['GET', 'POST'])
@login_required
def dashboard():

    return render_template('dashboard.html')

これでダッシュボードページはログインが必要になり、ログインページに自動的に遷移させられます。

 

その11 ログアウト機能を実装

# app.py

# ログアウト機能の設定
@app.route('/logout', methods=['GET', 'POST'])
@login_required
def logout():
    # logout_userメソッドを呼び出し
    logout_user()
    flash('ログアウトしました')
    return redirect(url_for('login'))

# dashboard.html

{% extends "base.html"%}

{% block content %}
<br/>
<h1>ダッシュボード</h1>
<br/>
<p>ログイン</p>
<a href="{{ url_for('logout')}}">ログアウト</a>


{% endblock %}

ログアウトのリンクを実装