暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Flask 用户指南 - 教程(4)- 蓝图和视图

数据库杂货铺 2022-02-19
409

蓝图和视图(Blueprint View

 

视图函数是为响应应用程序请求而编写的代码。Flask 使用模式将传入的请求 URL 与应该处理它的视图相匹配。Flask 将视图返回的数据转换为传出响应。Flask 也可以重定向,根据名称和参数生成一个指向视图的 URL

 

创建蓝图

 

蓝图是一种组织一组相关视图和其他代码的方法。它们不是直接向应用程序注册视图和其他代码,而是向蓝图注册。然后,当蓝图在工厂函数中可用时,它将在应用程序中注册。

 

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() 注册蓝图。将新代码放在工厂函数的末尾,返回 app 之前。

 

# flaskr/__init__.py
def create_app():
app = ...
# existing code omitted


from . import auth
app.register_blueprint(auth.bp)


return app
复制

 

鉴权蓝图将具有新用户注册、登录和注销的视图。

 

第一个视图:Register

 

当用户访问 /auth/register URL 时,register 视图将返回 HTML,此 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 视图函数要做的事情:

 

1. @bp.route URL /register register 视图函数相关联。当 Flask 收到对 /auth/register 的请求时,它将调用 register 视图并使用返回值作为响应。

 

2. 如果用户提交了表单,request.method 将是 'POST'。在这种情况下,开始验证输入数据。

 

3. request.form 是一种特殊类型的字典映射,包含表单键和值。用户将输入他们的 username password

 

4. 验证 username password 是否为空。

 

5. 如果验证成功,将新用户数据插入数据库。

 

db.execute 接受带有 ? 占位符的 SQL 查询,以及用于替换占位符的元组值。数据库组件将负责转义这些值,这样就不容易受到 SQL 注入攻击。

 

○ 为了安全起见,密码不应直接存储在数据库中。应该使用 generate_password_hash() 对密码进行哈希处理,并存储该哈希值。因为这个查询修改数据,所以需要调用 db.commit() 来保存更改。

 

○ 如果用户名已经存在,就会出现 sqlite3.IntegrityError 错误,这应该作为验证错误显示给用户。

 

6. 存储用户数据后,他们会被重定向到登录页面。url_for() 根据登录视图的名称生成其 URL。这比直接编写 URL 更好,因为它允许您以后更改 URL,而无需更改关联到它的所有代码。redirect() 重定向到生成的 URL

 

7. 如果验证失败,将向用户显示错误。flash() 存储呈现模板时可以检索的消息。

 

8. 当用户最初导航到 auth/register 时,或者出现验证错误时,应该显示带有注册表单的 HTML 页面。render_template() 将呈现包含 HTML 的模板,在本教程的后面将编写该模板。

 

Login

 

此视图与上面的 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 视图有一些不同:

 

1. 首先查询用户,并将其存储在一个变量中以供以后使用。

 

fetchone() 返回查询中的一行。如果查询没有返回任何结果,它将返回 None。然后调用 fetchall(),它返回所有结果的列表。

 

2. check_password_hash() 以与存储的哈希相同的方式对提交的密码进行哈希处理,并对其进行安全比较。如果它们匹配,则密码有效。

 

3. 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

 

Logout

 

要注销,需要从 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
复制

 

此装饰器返回一个新的视图函数,该函数包装它应用的原始视图。新函数检查用户是否已加载,否则将重定向到登录页面。如果加载了用户,则调用原始视图并正常继续下去。在编写博客视图时,将使用此装饰器。

 

端点(Endpoint)和 URL

 

url_for() 函数根据名称和参数生成视图的 URL。与视图关联的名称也称为端点,默认情况下,它与视图函数的名称相同。

 

例如,在本教程前面添加到应用程序工厂的 hello() 视图名为 'hello',可以通过 url_for('hello') 构造 URL。如果它有一个参数,可以如此构造 URL: url_for('hello', who='World')

 

使用蓝图时,蓝图的名称加在函数名之前,因此上面编写的 login 函数的端点是 'auth.login',因为已将其添加到 'auth' 蓝图中。

 

 

 

 

 

原文:

https://flask.palletsprojects.com/en/2.0.x/tutorial/views/


文章转载自数据库杂货铺,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论