蓝图和视图¶
视图函数是为响应对应用程序的请求而编写的代码。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() <Flask.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 injection attack 。出于安全考虑,密码不应直接存储在数据库中。相反,
generate_password_hash()
用于安全地对密码进行哈希处理,并存储该哈希。由于该查询修改数据,db.commit()
需要在之后调用以保存更改。一个
sqlite3.IntegrityError
如果用户名已经存在,则会发生该错误,这应该作为另一个验证错误显示给用户。
存储用户之后,它们将被重定向到登录页面。url_for 根据登录视图的名称为其生成URL。这比直接编写URL更好,因为它允许您稍后更改URL,而不更改链接到它的所有代码。 redirect() 为生成的 URL 生成一个重定向响应。
如果验证失败,则向用户显示错误。flash() 用于储存在渲染模块时可以检索的信息。
当用户最初访问 auth/register 时,或者注册出错时,应用显示一个注册表单。 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() <Blueprint.before_app_request> 注册在视图函数之前运行的函数,无论请求什么URL, load_logged_in_user
检查用户ID是否存储在`session` 从数据库中获取该用户的数据,并将其存储在`g.user <g>` 。g.user 的持续时间比请求要长。 如果没有用户 id,或者 id 不存在,那么 g.user 将会是 None 。
注销¶
注销的时候需要把用户 id 从 session 中移除。然后 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'``蓝图。
下面请阅读:templates .