蓝图和视图 — Flask 文档

来自菜鸟教程
Flask/docs/2.0.x/tutorial/views
跳转至:导航、​搜索

蓝图和视图

视图函数是您编写的用于响应应用程序请求的代码。 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 视图函数的作用:

  1. @bp.route 将 URL /registerregister 视图函数相关联。 当 Flask 收到对 /auth/register 的请求时,它会调用 register 视图并使用返回值作为响应。
  2. 如果用户提交了表单,request.method 将是 'POST'。 在这种情况下,开始验证输入。
  3. request.form 是一种特殊类型的 dict 映射提交的表单键和值。 用户将输入他们的 usernamepassword
  4. 验证 usernamepassword 不为空。
  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 的模板,您将在本教程的下一步中编写该模板。


登录

此视图遵循与上述 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 存储在 会话 中,它将在后续请求中可用。 在每个请求开始时,如果用户登录,他们的信息应该被加载并提供给其他视图。

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


登出

要注销,您需要从 会话 中删除用户 ID。 然后 load_logged_in_user 不会在后续请求中加载用户。

flaskr/auth.py

@bp.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('index'))

在其他视图中需要身份验证

创建、编辑和删除博客文章需要用户登录。 decorator 可用于检查它应用到的每个视图。

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',因为您将其添加到 【X196X】蓝图。

继续模板