備忘録です。
今回は、ログイン機能を実装します。
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 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()
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 %}
ログアウトのリンクを実装