flask login(blueprint 利用) - Flask

  • 作成日:
  • 最終更新日:2025/10/11

フォルダ・ファイル構成

以下のコマンドを実行し、必要なパッケージをインストールします。

pip install flask sqlalchemy flask-sqlalchemy flask-wtf mysql-connector-python pymysql
  • project
    • controllers
      • content.py
      • user.py
    • models
      • user.py
    • static
      • css
        • style.css
      • js
        • scripts.js
    • templates
      • content
        • index.html
      • share
        • layout.html
      • user
        • login.html
    • __init__.py
    • app.py
    • config.py
    • database.py

__init__.py

__init__.pyを置くことで、そのディレクトリ全体をFlaskアプリケーションとして定義します。

__init__.py

from flask import Flask, redirect, url_for, session, flash
from .database import db
from .config import *
from .controllers.user import user
from .controllers.content import content
from flask_wtf.csrf import CSRFProtect
from flask_login import LoginManager, login_required, logout_user, current_user
from .models.user import User

# <-- comment.login-manager
# flask-login の初期化
# クラスの作成
login_manager = LoginManager()

# /comment.login-manager -->

# <-- comment.login_required
# リダイレクトするビュー関数と、エラーメッセージを定義
# ビュー関数 login にリダイレクトするように指定する
login_manager.login_view = 'user.login'

# 未ログインユーザーにメッセージ表示
def localize_callback(*args, **kwarg):
return 'このページにアクセスするには、ログインが必要です'

login_manager.localize_callback = localize_callback
# /comment.login_required -->

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

def create_app():
app = Flask(__name__)
# session を使う際に SECRET_KEY を設定
app.config['SECRET_KEY'] = 'secret_key'

# CSRF 対策
csrf = CSRFProtect(app)
app.config.from_object(config.Config)
db.init_app(app)

app_name = [
    user,
    content
]

for i in app_name:
    app.register_blueprint(i)

login_manager.init_app(app)

# ログアウト処理
@app.route('/logout')
@login_required
def logout():
    logout_user()
    flash('ログアウトしました。') 
    return redirect(url_for('user.login'))

return app

register_blueprint()は、アプリを登録するメソッドです。

app.py

from . import create_app

app = create_app()

if __name__ == '__main__':
app.run()

config.py

class SystemConfig:
DEBUG = True

SQLALCHEMY_DATABASE_URI = 'mysql://{user}:{password}@{host}/{db_name}?charset=utf8'.format(**{
    'user': 'root',
    'password': 'password',
    'host': 'localhost',
    'db_name': 'sample' # データベース名
})

SQLALCHEMY_TRACK_MODIFICATIONS = "false"

Config = SystemConfig

database.py

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

def init_db(app):
db.init_app(app)

models/user.py

from .. import db
from flask_login import UserMixin

class User(UserMixin, db.Model):
__tablename__ = 'user'

id = db.Column(db.Integer, primary_key=True)
mail = db.Column(db.String(255), nullable=False)
password = db.Column(db.Integer, nullable=False)

controller

controllers/content.py

from flask import Blueprint, render_template, request, redirect
from flask_login import login_required

content = Blueprint("content", __name__, url_prefix='/content')

@content.route('/')
@login_required
def index():
return render_template('content/index.html')

controllers/user.py

from flask import Blueprint, render_template, request, redirect, url_for, session, flash
from sqlalchemy.exc import NoResultFound, MultipleResultsFound
from flask_login import login_user
from ..models.user import User
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
import bcrypt

user = Blueprint("user", __name__, url_prefix='/user')

class LoginForm(FlaskForm):
mail = StringField('mail')
password = PasswordField('Password')
submit = SubmitField()

@user.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
# POST の処理
if request.method == "POST":
    mail = request.form.get('mail')
    password = request.form.get('password')

    try:
        # Userテーブルから mail に一致するユーザを取得
        user = User.query.filter_by(mail=mail).one_or_none()
        
        if user is None:
            flash('ログインできませんでした。') 
            return redirect(url_for('user.login'))
        
        if bcrypt.checkpw(password.encode("utf8"), user.password.encode("utf8")):
            # ユーザー情報をセッションに格納
            login_user(user)
            return redirect('/content')
        else:
            flash('ログインできませんでした。') 
            return redirect(url_for('user.login'))
    except NoResultFound:
        flash('データがありません。') 
        return redirect(url_for('user.login'))
    except MultipleResultsFound:
        flash('データが複数あります。') 
        return redirect(url_for('user.login'))
else:
    # GET の処理
    return render_template('user/login.html', form = form)

static

static/css/style.css

li {color: red;}

static/js/scripts.js

templates

templates/content/index.html

{%- extends "./share/layout.html" %}
{%- block content %}
<h1>content/index</h1>
<p><a href="/logout">ログアウト</a></p>
<p>{{ message }}</p>
{% if current_user.is_authenticated %}
  <p>Hello, {{ current_user.mail }}.</p>
{% endif %}
{%- endblock %}

templates/share/layout.html

<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title></title>
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}" />
    <script src="{{ url_for('static', filename='js/scripts.js') }}"></script>
</head>
<body>
  <div class="content">
    <div id="main">
    {%- block content %} {% endblock %}
    </div>
  </div>
</body>
</html>

templates/user/login.html

{%- extends "./share/layout.html" %}
{%- block content %}
<h1>user/login</h1>
<ul>
  {% with messages = get_flashed_messages() %}
{% for message in messages %}
  <li>{{ message }}</li>
{% endfor %}
  {% endwith %}
</ul>

<form action="/user/login" method="post">
  {{ form.hidden_tag() }}
  <p>{{ form.mail.label }} {{ form.mail() }}</p>
  <p>{{ form.password.label }} {{ form.password() }}</p>
  <p>{{ form.submit() }}</p>
</form>
{%- endblock %}