flask login(blueprint 利用) - Flask

  • 作成日:
  • 最終更新日:2025/06/25

フォルダ・ファイル構成

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

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 %}