蓝图和视图¶
视图函数是您编写以响应应用程序请求的代码。Flask 使用模式将传入的请求 URL 与应处理该请求的视图进行匹配。视图返回数据,Flask 将其转换为传出响应。Flask 还可以反向进行,并根据视图的名称和参数生成一个 URL。
创建蓝图¶
一个 Blueprint
是组织一组相关视图和其他代码的一种方法。不是直接向应用程序注册视图和其他代码,而是向蓝图注册它们。然后在工厂函数中可用时,将蓝图注册到应用程序中。
Flaskr 将有两个蓝图,一个用于身份验证函数,另一个用于博客文章函数。每个蓝图的代码将放在一个单独的模块中。由于博客需要了解身份验证,因此您将首先编写身份验证。
flaskr/auth.py
¶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__
作为第二个参数传递。url_prefix
将被预先添加到与蓝图关联的所有 URL 中。
使用 app.register_blueprint()
从工厂导入并注册蓝图。在返回应用程序之前,将新代码放在工厂函数的末尾。
flaskr/__init__.py
¶def create_app():
app = ...
# existing code omitted
from . import auth
app.register_blueprint(auth.bp)
return app
认证蓝图将具有用于注册新用户以及登录和注销的视图。
第一个视图:注册¶
当用户访问 /auth/register
URL 时,register
视图将返回 HTML,其中包含一个供他们填写的表单。当他们提交表单时,它将验证他们的输入,并显示带有错误消息的表单或创建新用户并转到登录页面。
现在,你只需编写视图代码。在下一页中,你将编写模板以生成 HTML 表单。
flaskr/auth.py
¶@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.'
if error is None:
try:
db.execute(
"INSERT INTO user (username, password) VALUES (?, ?)",
(username, generate_password_hash(password)),
)
db.commit()
except db.IntegrityError:
error = f"User {username} is already registered."
else:
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
不为空。如果验证成功,将新用户数据插入数据库。
db.execute
采用 SQL 查询,其中?
占位符表示任何用户输入,以及一个值元组来替换占位符。数据库库将负责转义值,因此您不会受到SQL 注入攻击的攻击。出于安全考虑,密码绝不应直接存储在数据库中。相反,
generate_password_hash()
用于安全地对密码进行哈希处理,并存储该哈希值。由于此查询会修改数据,因此需要调用db.commit()
来保存更改。如果用户名已存在,将发生
sqlite3.IntegrityError
,应将此错误显示给用户作为另一个验证错误。
存储用户后,将他们重定向到登录页面。
url_for()
根据登录视图的名称生成 URL。这比直接编写 URL 更可取,因为它允许您稍后更改 URL,而无需更改所有链接到它的代码。redirect()
根据生成的 URL 生成重定向响应。如果验证失败,将错误显示给用户。
flash()
存储可在呈现模板时检索的消息。当用户最初导航到
auth/register
,或出现验证错误时,应显示包含注册表单的 HTML 页面。render_template()
将呈现包含 HTML 的模板,您将在本教程的下一步中编写此模板。
登录¶
此视图遵循与上面 register
视图相同的模式。
flaskr/auth.py
¶@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
视图相比,有一些区别
首先查询用户并将其存储在变量中以供以后使用。
fetchone()
从查询中返回一行。如果查询未返回任何结果,则返回None
。稍后,将使用fetchall()
,它将返回所有结果的列表。check_password_hash()
以与存储的哈希相同的方式对提交的密码进行哈希处理,并对其进行安全比较。如果匹配,则密码有效。session
是一个dict
,用于跨请求存储数据。当验证成功时,用户的id
会存储在一个新的会话中。数据存储在发送到浏览器的 cookie 中,然后浏览器在后续请求中将其发送回来。Flask 会安全地 签名 数据,以防止数据被篡改。
现在,用户的 id
已存储在 session
中,它将在后续请求中可用。在每个请求的开始,如果用户已登录,则应加载其信息并将其提供给其他视图。
flaskr/auth.py
¶@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
检查 session
中是否存储了用户 ID,并从数据库中获取该用户的数据,将其存储在 g.user
上,该数据在请求期间一直存在。如果没有用户 ID,或者该 ID 不存在,则 g.user
将为 None
。
注销¶
要注销,您需要从 session
中删除用户 ID。然后,load_logged_in_user
不会在后续请求中加载用户。
flaskr/auth.py
¶@bp.route('/logout')
def logout():
session.clear()
return redirect(url_for('index'))
在其他视图中要求身份验证¶
创建、编辑和删除博客文章需要用户登录。可以使用装饰器检查应用于每个视图的此项内容。
flaskr/auth.py
¶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.login'
,因为您已将其添加到 'auth'
蓝图。
继续到 模板。