蓝图和视图¶
视图函数是您编写的用于响应应用程序请求的代码。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
检查用户 ID 是否存储在 session
中,并从数据库中获取该用户的数据,将其存储在 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'
蓝图。
继续阅读 模板。