フォルダ・ファイル構成
以下のコマンドを実行し、必要なパッケージをインストールします。
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
- css
- templates
- content
- index.html
- share
- layout.html
- user
- login.html
- content
- __init__.py
- app.py
- config.py
- database.py
- controllers
__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 %}