ブループリントとビュー
ビュー関数は、アプリケーションへの要求に応答するために作成するコードです。 Flaskはパターンを使用して、受信リクエストURLをそれを処理するビューに一致させます。 ビューは、Flaskが発信応答に変換するデータを返します。 Flaskは逆方向に進み、名前と引数に基づいてビューへのURLを生成することもできます。
ブループリントを作成する
Blueprint
は、関連するビューやその他のコードのグループを整理する方法です。 ビューやその他のコードをアプリケーションに直接登録するのではなく、ブループリントで登録します。 次に、ブループリントは、ファクトリ関数で使用可能になったときにアプリケーションに登録されます。
Flaskrには2つの青写真があります。1つは認証機能用で、もう1つはブログ投稿機能用です。 各ブループリントのコードは、個別のモジュールに含まれます。 ブログは認証について知る必要があるので、最初に認証を記述します。
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from flaskr.db import get_db
bp = Blueprint('auth', __name__, url_prefix='/auth')
これにより、'auth'
という名前のBlueprint
が作成されます。 アプリケーションオブジェクトと同様に、ブループリントはそれがどこで定義されているかを知る必要があるため、__name__
が2番目の引数として渡されます。 url_prefix
は、ブループリントに関連付けられているすべてのURLの先頭に追加されます。
app.register_blueprint()
を使用して、工場からブループリントをインポートして登録します。 アプリを返す前に、ファクトリ関数の最後に新しいコードを配置します。
def create_app():
app = ...
# existing code omitted
from . import auth
app.register_blueprint(auth.bp)
return app
認証ブループリントには、新しいユーザーを登録し、ログインおよびログアウトするためのビューがあります。
最初のビュー:登録
ユーザーが/auth/register
URLにアクセスすると、register
ビューは HTML とフォームを返します。 フォームを送信すると、入力が検証され、エラーメッセージが表示されてフォームが再度表示されるか、新しいユーザーを作成してログインページに移動します。
今のところ、ビューコードを書くだけです。 次のページでは、HTMLフォームを生成するためのテンプレートを作成します。
@bp.route('/register', methods=('GET', 'POST'))
def register():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
db = get_db()
error = None
if not username:
error = 'Username is required.'
elif not password:
error = 'Password is required.'
elif db.execute(
'SELECT id FROM user WHERE username = ?', (username,)
).fetchone() is not None:
error = 'User {} is already registered.'.format(username)
if error is None:
db.execute(
'INSERT INTO user (username, password) VALUES (?, ?)',
(username, generate_password_hash(password))
)
db.commit()
return redirect(url_for('auth.login'))
flash(error)
return render_template('auth/register.html')
register
ビュー関数の機能は次のとおりです。
@bp.route
は、URL/register
をregister
ビュー機能に関連付けます。 Flaskは、/auth/register
へのリクエストを受信すると、register
ビューを呼び出し、戻り値を応答として使用します。ユーザーがフォームを送信した場合、
request.method
は'POST'
になります。 この場合、入力の検証を開始します。request.form
は、送信されたフォームのキーと値をマッピングする特別なタイプのdict
です。 ユーザーはusername
とpassword
を入力します。username
とpassword
が空でないことを確認します。データベースにクエリを実行し、結果が返されるかどうかを確認して、
username
がまだ登録されていないことを確認します。db.execute
は、任意のユーザー入力に対して?
プレースホルダーを使用し、プレースホルダーを置き換える値のタプルを使用してSQLクエリを取得します。 データベースライブラリが値のエスケープを処理するため、 SQLインジェクション攻撃に対して脆弱ではありません。fetchone()
は、クエリから1行を返します。 クエリが結果を返さなかった場合は、None
を返します。 その後、fetchall()
が使用され、すべての結果のリストが返されます。検証が成功した場合は、新しいユーザーデータをデータベースに挿入します。 セキュリティ上の理由から、パスワードをデータベースに直接保存しないでください。 代わりに、
generate_password_hash()
を使用してパスワードを安全にハッシュし、そのハッシュを保存します。 このクエリはデータを変更するため、変更を保存するには、後でdb.commit()
を呼び出す必要があります。ユーザーを保存した後、ユーザーはログインページにリダイレクトされます。
url_for()
は、名前に基づいてログインビューのURLを生成します。 これは、URLに直接リンクするすべてのコードを変更せずに、後でURLを変更できるため、URLを直接書き込むよりも望ましい方法です。redirect()
は、生成されたURLへのリダイレクト応答を生成します。検証が失敗した場合、エラーがユーザーに表示されます。
flash()
は、テンプレートのレンダリング時に取得できるメッセージを格納します。ユーザーが最初に
auth/register
に移動したとき、または検証エラーが発生したときは、登録フォームを含むHTMLページが表示されます。render_template()
は、チュートリアルの次のステップで作成するHTMLを含むテンプレートをレンダリングします。
ログイン
このビューは、上記のregister
ビューと同じパターンに従います。
@bp.route('/login', methods=('GET', 'POST'))
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
db = get_db()
error = None
user = db.execute(
'SELECT * FROM user WHERE username = ?', (username,)
).fetchone()
if user is None:
error = 'Incorrect username.'
elif not check_password_hash(user['password'], password):
error = 'Incorrect password.'
if error is None:
session.clear()
session['user_id'] = user['id']
return redirect(url_for('index'))
flash(error)
return render_template('auth/login.html')
register
ビューとはいくつかの違いがあります。
- ユーザーは最初に照会され、後で使用するために変数に格納されます。
check_password_hash()
は、保存されたハッシュと同じ方法で送信されたパスワードをハッシュし、それらを安全に比較します。 それらが一致する場合、パスワードは有効です。- session は、リクエスト間でデータを保存する
dict
です。 検証が成功すると、ユーザーのid
が新しいセッションに保存されます。 データはブラウザに送信される cookie に保存され、ブラウザはその後のリクエストでデータを送り返します。 データが改ざんされないように、データを安全に署名します。
ユーザーのid
がセッションに保存されたので、以降のリクエストで利用できるようになります。 各リクエストの開始時に、ユーザーがログインしている場合は、情報を読み込んで他のビューで利用できるようにする必要があります。
@bp.before_app_request
def load_logged_in_user():
user_id = session.get('user_id')
if user_id is None:
g.user = None
else:
g.user = get_db().execute(
'SELECT * FROM user WHERE id = ?', (user_id,)
).fetchone()
bp.before_app_request()
は、要求されたURLに関係なく、ビュー関数の前に実行される関数を登録します。 load_logged_in_user
は、ユーザーIDがセッションに保存されているかどうかを確認し、そのユーザーのデータをデータベースから取得して、 g.user に保存します。リクエスト。 ユーザーIDがない場合、またはIDが存在しない場合、g.user
はNone
になります。
ログアウト
ログアウトするには、セッションからユーザーIDを削除する必要があります。 その場合、load_logged_in_user
は後続のリクエストでユーザーをロードしません。
@bp.route('/logout')
def logout():
session.clear()
return redirect(url_for('index'))
他のビューで認証が必要
ブログ投稿を作成、編集、削除するには、ユーザーがログインする必要があります。 デコレータを使用して、適用されているビューごとにこれを確認できます。
def login_required(view):
@functools.wraps(view)
def wrapped_view(**kwargs):
if g.user is None:
return redirect(url_for('auth.login'))
return view(**kwargs)
return wrapped_view
このデコレータは、適用された元のビューをラップする新しいビュー関数を返します。 新しい関数は、ユーザーがロードされているかどうかを確認し、ロードされていない場合はログインページにリダイレクトします。 ユーザーがロードされると、元のビューが呼び出され、通常どおり続行されます。 ブログビューを作成するときに、このデコレータを使用します。
エンドポイントとURL
url_for()
関数は、名前と引数に基づいてビューへのURLを生成します。 ビューに関連付けられた名前はエンドポイントとも呼ばれ、デフォルトではビュー関数の名前と同じです。
たとえば、チュートリアルの前半でアプリファクトリに追加されたhello()
ビューは、'hello'
という名前で、url_for('hello')
とリンクできます。 後で説明する引数を取ると、url_for('hello', who='World')
を使用するようにリンクされます。
ブループリントを使用する場合、ブループリントの名前が関数の名前の前に付加されるため、上記で記述したlogin
関数のエンドポイントは、'auth'
青写真。
テンプレートに進みます。