美文网首页
Flask官方文档中的留言板项目解说之auth.py说明(4)

Flask官方文档中的留言板项目解说之auth.py说明(4)

作者: ArthurIsUsed | 来源:发表于2020-10-29 09:42 被阅读0次

说明

用户的认证,有登录、登出、注册,但是编辑留言板需要确保是登录状态。遇到大的项目,很多地方都需要是登录状态,每次先判断是否登录再执行相关操作会很麻烦。因此需要一个登录要求的函数,作为装饰器。

通常,用户登录后会把登录信息记录到session中,再次登录就可以从session中查找是否有此用户,这样可以降低服务器的压力。那么问题又来了,在请求视图前要求,还是到达服务器之前就要求呢,这里就要用到Flask的4个钩子函数中的一个。

分析完后,就清楚,这个auth.py文件需要用到的函数是

  • register() 用户注册
  • login() 用户登录
  • load_logged_in_user() 判断用户登录信息是否在session中
  • logout() 用户登出
  • login_required() 装饰器函数,要求编辑前是登录状态
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")


@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."
        elif db.execute(
            "SELECT id FROM user WHERE username = ?", (username,)
        ).fetchone() is not None:
            error = "User {} is already register.".format(username)

        if error is None:
            db.execute(
                "INSERT INTO user (username, password) VALUES (?, ?)",
                (username, generate_password_hash(password))
            )
            db.commit()
            return redirect(url_for('auth.login'))

        flash(error)

    return render_template("auth/register.html")


@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')


@bp.before_app_request
def load_logged_in_user():
    """ 客户端请求服务器之前,需要先运行这个函数,判断用户是否再session中"""
    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.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('index'))


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

总结

Blueprint在上节已做说明,不再细讲。

register函数,先判断http的方法是否是post,即是否有点击register按钮。若是,先保留用户名密码,判断是否为空,不为空则验证用户名是否在数据库中。error是一个标志,前几步都OK,error会是None,则把注册的用户名密码插入到user表中,再跳转到登录页面。当然,也有的项目会要求,正常注册后就已经是登录状态,那么redirect函数可以重定向到index。

login函数,上同,表中有username,重新刷新session后就跳转到index页面。

带有钩子函数装饰器的load_logged_in_user()函数,表示客户端请求视图前需要的操作。下篇重点介绍应用钩子函数、Blueprint钩子函数。

logout函数, 退出时清理session即可,下次登录时,session中没有user_id,就需要重新输入用户名密码。

login_required函数,起到装饰器的作用。比如在delete函数上添加@login_required, 即当执行delete函数前会先执行login_required,如果未登录就跳转到auth.login,要求用户登录。如果没有functools.wraps(view) 函数的函数名会被装饰器改变,当一个函数被两个装饰器装饰时,由于函数名不一致,会报错。而functools.wraps则把装饰器改过的函数名再改会原函数名。

无function.wraps的代码如下:

def description(f):
    def wrapper(*args, **kwargs):
        return f(*args, **kwargs)

    return wrapper


@description
def num1():
    print(num1.__name__)


@description
def num2():
    print(num2.__name__)


if __name__ == '__main__':
    num1()
    num2()

输出结果为:
wrapper
wrapper

添加functools.wraps后:

import functools


def description(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        return f(*args, **kwargs)

    return wrapper


@description
def num1():
    print(num1.__name__)


@description
def num2():
    print(num2.__name__)


if __name__ == '__main__':
    num1()
    num2()

输出结果为:
num1
num2

相关文章

网友评论

      本文标题:Flask官方文档中的留言板项目解说之auth.py说明(4)

      本文链接:https://www.haomeiwen.com/subject/mrvvmktx.html