Flask项目实战——5—(角色定义、用户权限、用户权限渲染到个人页面、客户端权限验证、服务端权限验证)

1、角色定义

创建角色定义的函数,并添加到数据库中:
命令行添加角色信息:manage.py

# -*- encoding: utf-8 -*-
"""
@File    : manage.py
@Time    : 2020/5/10 17:36
@Author  : chen

"""
from flask_script import Manager
from bbs import app     # 需要将当前文件夹设置为当前根目录,才不会报错
from flask_migrate import Migrate, MigrateCommand
from exts import db

# 导入模型 才能映射到数据库     导入后端的模型
from apps.cms.models import CMS_User

# 导入角色模型,映射到数据库         CMSPersmission角色权限定义类
from apps.cms.models import CMSRole, CMSPersmission

manage = Manager(app)

Migrate(app, db)
manage.add_command('db', MigrateCommand)


# 命令行添加后端用户
@manage.option('-u', '--username', dest='username')
@manage.option('-p', '--password', dest='password')
@manage.option('-e', '--email', dest='email')
def create_cms_user(username, password, email):
    user = CMS_User(username=username, password=password, email=email)
    # 添加映射到数据库,提交至数据库
    db.session.add(user)
    db.session.commit()
    print("cms后端用户添加成功")


# 添加角色  不传参用command,传参用option
@manage.command
def create_role():
    # 访问者
    visitor = CMSRole(name="访问者", desc="只能查看数据,不能修改数据")
    visitor.permission = CMSPersmission.VISITOR                         # 权限
    
    # 运营人员
    operator = CMSRole(name="运营人员", desc="管理评论、帖子、管理前台用户")
    # 权限或运算,代表包含有运算中的所有权限     二进制的运算 001|010=011
    operator.permission = CMSPersmission.VISITOR | CMSPersmission.POSTER | CMSPersmission.CMSUSER | \
                          CMSPersmission.COMMENTER | CMSPersmission.FRONTUSER
    
    # 管理员
    admin = CMSRole(name="管理员", desc="拥有本系统大部分权限")
    admin.permission = CMSPersmission.VISITOR | CMSPersmission.POSTER | CMSPersmission.CMSUSER | \
                          CMSPersmission.COMMENTER | CMSPersmission.FRONTUSER | CMSPersmission.BOARDER

    # 开发人员
    developer = CMSRole(name="开发人员", desc="拥有本系统所有权限")
    developer.permission = CMSPersmission.ALL_PERMISSION
    
    # 提交数据库   添加身份字段到数据库中的表,
    db.session.add_all([visitor, operator, admin, developer])
    db.session.commit()
    return "创建角色成功"


if __name__ == '__main__':
    manage.run()

实现效果如下:
在这里插入图片描述

2、用户权限

判断用户所具有的权限:模型文件apps/cms/models.py

# -*- encoding: utf-8 -*-
"""
@File    : models.py
@Time    : 2020/5/11 10:00
@Author  : chen

"""
# 定义后端用户模型
from exts import db                                                               # 数据库
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash         # 导入密码加密,解密方法的库


# 权限定义,不是模型,没有继承db.Model
class CMSPersmission(object):
    # 255 二进制表示所有的权限
    ALL_PERMISSION = 0b11111111          # 每一位数代表一个权限,共7个权限,8位1个字节
    
    # 访问权限
    VISITOR        = 0b00000001
    
    # 管理帖子
    POSTER         = 0b00000010
    
    # 管理评论
    COMMENTER      = 0b00000100
    
    # 管理板块
    BOARDER        = 0b00001000
    
    # 管理后台用户
    CMSUSER        = 0b00010000
    # 管理前台用户
    FRONTUSER      = 0b00100000
    # 管理管理员用户
    ADMINER        = 0b01000000


# 权限与角色是多对多的关系,创建他们的中间表
cms_role_user = db.Table(
    "cms_role_user",
    db.Column("cms_role_id", db.Integer, db.ForeignKey('cms_role.id'), primary_key=True),
    db.Column("cms_user_id", db.Integer, db.ForeignKey('cms_user.id'), primary_key=True),
)


# 角色模型定义   继承了db.Model
class CMSRole(db.Model):
    __tablename__ = 'cms_role'
    
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)      # 主键  自增
    name = db.Column(db.String(50), nullable=False)                       # 非空
    desc = db.Column(db.String(250), nullable=False)                      # 非空
    creat_time = db.Column(db.DateTime, default=datetime.now)
    permission = db.Column(db.Integer, default=CMSPersmission.VISITOR)    # 默认先给游客权限

    # 反向查询属性,关联中间表secondary=cms_role_user,对应了CMS_User模型,建立模型联系,不映射到数据库中
    users = db.relationship('CMS_User', secondary=cms_role_user, backref="roles")    # roles是CMS_User的外键
    
    
# 后台用户模型定义
class CMS_User(db.Model):
    __tablename__ = 'cms_user'
    
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)           # 主键  自增
    username = db.Column(db.String(150), nullable=False)                       # 非空
    # password = db.Column(db.String(150), nullable=False)
    _password = db.Column(db.String(150), nullable=False)                      # 密码加密操作修改字段
    email = db.Column(db.String(50), nullable=False, unique=True)              # 非空、唯一
    join_time = db.Column(db.DateTime, default=datetime.now)                   # 默认当前时间
    
    # 修改密码加密操作中的字段,在manage.py映射数据库时候,使用字段还是保持相同
    def __init__(self, username, password, email):
        self.username = username
        self.password = password         # 调用该方法 返回下面的self._password数值,
        self.email = email
    
    # 密码加密操作
    @property
    def password(self):                   # 密码取值
        return self._password

    @password.setter                      # 密码加密
    def password(self, raw_password):
        self._password = generate_password_hash(raw_password)

    # 用于验证后台登录密码是否和数据库一致,raw_password是后台登录输入的密码
    def check_password(self, raw_password):
        result = check_password_hash(self.password, raw_password)   # 相当于用相同的hash加密算法加密raw_password,检测与数据库中是否一致
        return result
    
    # 封装用户的权限
    @property
    def permission(self):
        if not self.roles:           # 反向查询属性,backref="roles",
            return 0                 # 没有任何权限
        
        # 所有权限
        all_permissions = 0
        
        for role in self.roles:                    # 循环调用所有角色
            permissions = role.permission         # 将这个角色的权限都取出来  role.permission代表CMSRole中的属性
            all_permissions |= permissions         # 当前这个角色的权限都在all_permissions
            
        return all_permissions
    
    # 判断用户所具有的权限
    def has_permissions(self, permission):
        all_permissions = self.permission                 # 调用permission(self)方法
        #  若所有权限0b11111111 & 用户权限     等于 本身,则代表具有该权限
        result = all_permissions & permission == permission
        # print(result)
        return result
        
    # 判断是否是开发人员
    @property
    def is_developer(self):
         return self.has_permissions(CMSPersmission.ALL_PERMISSION)       # 调用has_permissions方法并传入所有权限
               

命令行添加用户到角色

添加测试用户权限方法:命令行添加用户到角色里面manage.py

# -*- encoding: utf-8 -*-
"""
@File    : manage.py
@Time    : 2020/5/10 17:36
@Author  : chen

"""
from flask_script import Manager
from bbs import app     # 需要将当前文件夹设置为当前根目录,才不会报错
from flask_migrate import Migrate, MigrateCommand
from exts import db

# 导入模型 才能映射到数据库     导入后端的模型
from apps.cms.models import CMS_User

# 导入角色模型,映射到数据库         CMSPersmission角色权限定义类
from apps.cms.models import CMSRole, CMSPersmission

manage = Manager(app)

Migrate(app, db)
manage.add_command('db', MigrateCommand)


# 命令行添加后端用户
@manage.option('-u', '--username', dest='username')
@manage.option('-p', '--password', dest='password')
@manage.option('-e', '--email', dest='email')
def create_cms_user(username, password, email):
    user = CMS_User(username=username, password=password, email=email)
    # 添加映射到数据库,提交至数据库
    db.session.add(user)
    db.session.commit()
    print("cms后端用户添加成功")


# 添加角色  不传参用command
@manage.command
def create_role():
    # 访问者
    visitor = CMSRole(name="访问者", desc="只能查看数据,不能修改数据")
    visitor.permission = CMSPersmission.VISITOR                         # 权限
    
    # 运营人员
    operator = CMSRole(name="运营人员", desc="管理评论、帖子、管理前台用户")
    # 权限或运算,代表包含有运算中的所有权限     二进制的运算 001|010=011
    operator.permission = CMSPersmission.VISITOR | CMSPersmission.POSTER | CMSPersmission.CMSUSER | \
                          CMSPersmission.COMMENTER | CMSPersmission.FRONTUSER
    
    # 管理员
    admin = CMSRole(name="管理员", desc="拥有本系统大部分权限")
    admin.permission = CMSPersmission.VISITOR | CMSPersmission.POSTER | CMSPersmission.CMSUSER | \
                          CMSPersmission.COMMENTER | CMSPersmission.FRONTUSER | CMSPersmission.BOARDER

    # 开发人员
    developer = CMSRole(name="开发人员", desc="拥有本系统所有权限")
    developer.permission = CMSPersmission.ALL_PERMISSION
    
    # 提交数据库   添加身份字段到数据库中的表,
    db.session.add_all([visitor, operator, admin, developer])
    db.session.commit()
    return "创建角色成功"


# 测试用户权限
@manage.command
def test_permission():
    # user = CMS_User.query.first()                          # 查询第一个用户,当时创建的用户还没有关联权限,所以应该是没有权限
    user = CMS_User.query.get(3)
    print(user)                                              # 显示用户信息
    if user.has_permissions(CMSPersmission.VISITOR):         # has_permissions方法判定是否具有该权限
        print("这个用户有访问者的权限!")
    else:
        print("这个用户有访问者的权限!")


# 添加用户到角色里面
@manage.option("-e", "--email", dest="email")
@manage.option("-n", "--name", dest="name")
def add_user_to_role(email, name):
    user = CMS_User.query.filter_by(email=email).first()                   # 通过邮箱查询用户
    if user:
        role = CMSRole.query.filter_by(name=name).first()                  # 邮箱存在的前提下,通过name查询角色
        if role:
            role.users.append(user)                                        # 将用户添加到角色中,list类型数据,role.users是CMSRole中的外键
            db.session.commit()                                            # 映射到数据库
            print("用户添加到角色成功")
        else:
            print("该角色不存在")
    else:
        print("邮箱不存在")


if __name__ == '__main__':
    manage.run()

测试效果如下:
在这里插入图片描述

3、用户权限渲染到个人页面

用户权限渲染到个人中心页面cms_profile.html

<!--  继承模板文件cms/cms_base.html  简化代码 -->
{% extends 'cms/cms_base.html' %}

<!-- 页面标题 -->
{% block title %}
    个人信息
{% endblock %}

<!--  标题  -->
{% block page_title %}
    {{self.title()}}
{% endblock %}


{% block content %}
    <!--  将全局变量的对象在这个block模块中命名为user  -->
    {% set user = g.cms_user %}
    <table class="table table-bordered">
            <tr>
                <td>用户名:</td>
                <!--  调用全局变量对象的username字段属性  user替代g.cms_user       -->
                <td>{{ user.username }}</td>
            </tr>
            <tr>
                <td>邮箱:</td>
                <td>{{ user.email }}</td>
            </tr>
            <tr>
                <td>角色:</td>
                <td>
                    <!--      循环 全局变量对象的外键roles      一个用户多个角色       -->
                    {% for role in user.roles %}
                        {{ role.name }}
                    {% endfor %}
                </td>
            </tr>
            <tr>
                <td>权限:</td>
                <td>
                    <!--     循环 全局变量对象的外键roles        -->
                    {% for role in user.roles %}
                        {{ role.desc }}
                    {% endfor %}
                </td>
            </tr>
            <tr>
                <td>加入时间:</td>
                <td>{{ user.join_time }}</td>
            </tr>
    </table>
{% endblock %}

渲染效果如下:
在这里插入图片描述

4、客户端权限验证

hooks.py文件添加钩子函数中的上下文处理器:返回的字典中的键可以在模板上下文中使用,传输到模板文件中调用

# -*- encoding: utf-8 -*-
"""
@File    : hooks.py
@Time    : 2020/5/13 9:36
@Author  : chen

"""
from flask import request, session, url_for, redirect, g   # g对象全局变量gloabl,方便调用
from .views import cms_bp
from .models import CMS_User
from .models import CMSPersmission                         # 导入CMSPersmission到上下文管理器中,方便全局调用


# 钩子函数 ,所有操作前执行该方法,判断当前界面是否是登录界面,不是就将url重定向到登录界面
@cms_bp.before_request
def before_request():
    print(request.path)                                         # 输出的是网页url的后缀,即/cms/login/
    if not request.path.endswith(url_for('cms.login')):         # 判断当前所在url是否是/cms/login/,不是代表不在后台登录界面
        user_id = session.get('user_id')                        # 登陆之后,获取登录时候记录的session中的user_id
        if not user_id:                                         # 若没有user_id,说明登录不成功
            return redirect(url_for('cms.login'))               # 重定向到后台登录界面

    # 判断user_id是否登陆过,登录之后就返回用户名到CMS后台管理系统
    if 'user_id' in session:
        user_id = session.get('user_id')                        # 调用session中user_id
        user = CMS_User.query.get(user_id)                      # 通过user_id查询到用户对象,方便前端界面调用对象中的字段属性
        if user:
            g.cms_user = user                                   # 赋值给g对象,全局变量g.cms_user用于渲染到后台管理界面cms_index.html

# 上面的代码相对于下面的来说比较简单,下面的是将对象中的字段属性单独来调用并修改为全局变量,上面只是将完整的一个对象变成全局变量
'''
    # 判断user_id是否登陆过,登录之后就返回用户名到CMS后台管理系统
    if 'user_id' in session:                              # user_id在session中,说明cms用户已经登录了
        user_name = session.get('user_name')              # 从session中调用user_username
        user_email = session.get('user_email')            # 从session中调用user_email,用于设置为全局变量,渲染到cms_profile.html中
        user_join_time = session.get('user_join_time')    # 从session中调用user_join_time,用于设置为全局变量,渲染到cms_profile.html中

        if user_name:
            g.cms_user = user_name                        # 赋值给g对象,全局变量g.cms_user用于渲染到后台管理界面cms_index.html
            g.cms_email = user_email                      # 设置为全局变量,渲染到cms_profile.html中
            g.cms_join_time = user_join_time              # 设置为全局变量,渲染到cms_profile.html中
'''

# 上下文处理器,返回的字典中的键可以在模板上下文中使用。
@cms_bp.context_processor
def cms_context_processor():
    return {"CMSPersmission": CMSPersmission}             # 模板上下文中使用 cms_base.html中使用

模板文件:templates/cms/cms_base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!--  在头文件中接收csrf信息  -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{% block title %}

    {% endblock %}</title>
    <script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
    <link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

<!--  关联本地的cms_base.css样式 后台管理界面CMS的样式 -->
    <link rel="stylesheet" href="{{ url_for('static', filename='cms/css/cms_base.css') }}">
<!--  关联本地的cms_base.js样式 后台管理界面CMS的样式 -->
    <script src="{{ url_for('static', filename='cms/js/cms_base.js') }}"></script>

<!--  提示框的静态资源文件  -->
    <link rel="stylesheet" href="{{ url_for('static', filename='common/sweetalert/sweetalert.css') }}">
<!-- 关联提示框的js样式  -->
    <script src="{{ url_for('static', filename='common/sweetalert/lgalert.js') }}"></script>
    <script src="{{ url_for('static', filename='common/sweetalert/sweetalert.min.js') }}"></script>

<!--  预留空间,给之后的html文件进行修改调整  -->
    {% block head %}

    {% endblock %}

</head>
<body>
     <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">论坛CMS管理系统</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
          <ul class="nav navbar-nav navbar-right">
        <!--       从数据库中调用用户名,g对象全局调用g.cms_user对象  .username是该对象的一个字段属性      -->
            <li><a href="#">{{ g.cms_user.username }}</a></li>

        <!--  用户注销,关联到views.py中的@cms_bp.route("/logout/")路由,重定向到该路由      -->
            <li><a href="{{ url_for('cms.logout') }}">注销</a></li>
          </ul>
          <form class="navbar-form navbar-right">
            <input type="text" class="form-control" placeholder="查找...">
          </form>
        </div>
      </div>
    </nav>

    <div class="container-fluid">
      <div class="row">
          <div class="col-sm-3 col-md-2 sidebar">
              <ul class="nav-sidebar">
                <li class="unfold"><a href="#">首页</a></li>
                <li class="profile-li">
                    <a href="#">个人中心<span></span></a>
                    <ul class="subnav">

                        <!--          url重定向到/cms/profile/下   路由在views.py中定义了       -->
                        <li><a href="{{ url_for('cms.profile') }}">个人信息</a></li>
                        <!--         密码修改的url_for 重定向到/cms/resetpwd/  路由在views.py中定义了           -->
                        <li><a href="{{ url_for('cms.resetpwd') }}">修改密码</a></li>
                        <!--         重定向到修改邮箱的url_for=/cms/resetemail/        -->
                        <li><a href="{{ url_for('cms.resetemail') }}">修改邮箱</a></li>
                    </ul>
                </li>

                  <!--  将全局变量的对象命名为user  -->
                  {% set user = g.cms_user %}
                  <!--    判断是否有权限进行管理后台,CMSPersmission.ALL_PERMISSION并没有传输过来,无法识别,需要用到钩子函数中的上下文管理器,在hooks.py中编写 -->
                  {% if  user.has_permissions(CMSPersmission.POSTER) %}
                    <li class="nav-group post-manage"><a href="#">帖子管理</a></li>
                  {% endif %}

                  {% if  user.has_permissions(CMSPersmission.COMMENTER) %}
                    <li class="comments-manage"><a href="#">评论管理</a></li>
                  {% endif %}

                  {% if  user.has_permissions(CMSPersmission.BOARDER) %}
                    <li class="board-manage"><a href="#">板块管理</a></li>
                  {% endif %}

                  {% if  user.has_permissions(CMSPersmission.FRONTUSER) %}
                    <li class="nav-group user-manage"><a href="#">前台用户管理</a></li>
                  {% endif %}

                  {% if  user.has_permissions(CMSPersmission.CMSUSER) %}
                    <li class="nav-group cmsuser-manage"><a href="#">CMS用户管理</a></li>
                  {% endif %}

                  {% if  user.is_developer %}
                    <li class="cmsrole-manage"><a href="#">CMS组管理</a></li>
                  {% endif %}

            </ul>
          </div>
          <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
            <h1>{% block page_title %}

            {% endblock %}</h1>
            <div class="main_content">
                {% block content %}

                {% endblock %}
            </div>
          </div>
      </div>
    </div>
</body>
</html>

5、服务端权限验证

装饰器文件用于验证用户不同的权限问题:apps/cms/decorators.py

# -*- encoding: utf-8 -*-
"""
@File    : decorators.py
@Time    : 2020/5/12 22:38
@Author  : chen

"""
# 装饰器方法实现另一种判定后台用户当前界面是否是登录界面,不是就重定向到登录界面
from flask import session, g
from flask import redirect, url_for
# 双层装饰器修饰
from functools import wraps


def login_required(func):
    def inner(*args, **kwargs):              # 内层函数
        if 'user_id' in session:
            return func(*args, **kwargs)
        else:
            return redirect(url_for("cms.login"))
    return inner


# 装饰器传参   判定用户不同模块的权限,进行验证显示
def permission_required(permission):
    def outter(func):
        # print(func)                                    # <function posts at 0x0000018950B76BF8>,views.py文件中的posts路由
        # print(type(func))                              # <class 'function'> 类
        @wraps(func)                                     # func需要传参,就需要用wraps
        def inner(*args, **kwargs):                      # 内层函数
            user = g.cms_user
            if user.has_permissions(permission):         # 判断g对象的权限
                return func(*args, **kwargs)             # 执行该方法
            else:
                return redirect(url_for('cms.index'))    # 跳转回首页/cms/index/
        return inner
    return outter

视图文件apps/cms/views.py文件添加装饰器验证服务端权限

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen

"""
# 蓝图文件:实现模块化应用,应用可以分解成一系列的蓝图   后端的类视图函数写在这个文件
from flask import (
    request, redirect, url_for,                  # 页面跳转redirect   request请求收集
    Blueprint, render_template, views, session,   # 定义类视图,显示模板文件
    jsonify, g                                       # jsonify强制转换成json数据
)
from exts import db, mail                            # 数据库中更新密码、邮箱等使用

# 导入form表单   .forms代表同级目录下的forms.py       ResetPwdForm修改密码的form信息
from .forms import LoginForm, ResetPwdForm
# 导入forms.py文件中的邮箱验证的表单信息类
from apps.cms.forms import ResetEmailForm

# 导入模型  .models代表同级目录下的models.py   CMSPersmission验证用户不同模块权限
from .models import CMS_User, CMSPersmission, CMSRole
from .decorators import permission_required            # 传参装饰器验证用户不同模块权限

# 导入装饰器:判断当前界面是否是登录界面,不是就将url重定向到登录界面,一般不用,使用的主要是钩子函数
from .decorators import login_required

# 导入restful.py中的访问网页状态码的函数          redis_captcha:redis存储、提取、删除验证码功能
from utils import restful, random_captcha, redis_captcha           # 随机生成验证码函数random_captcha()

# 导入flask-mail中的Message
from flask_mail import Message

cms_bp = Blueprint("cms", __name__, url_prefix='/cms/')     # URL前缀url_prefix

# 钩子函数是在cms_bp创建之后才创建的,顺序在cms_bp创建之后
from .hooks import before_request


@cms_bp.route("/")                                          # 后台界面
# @login_required             # 装饰器判定当前界面是否是登录界面,但是需要每个路由函数都要加该装饰器,比较麻烦,推荐使用钩子函数
def index():
    # return "cms index:后端类视图文件"
    return render_template('cms/cms_index.html')       # 登陆之后进入CMS后台管理界面,路径写全cms/cms_index.html


# 用户注销登录
@cms_bp.route("/logout/")                              # 需要关联到cms/cms_index.html中的注销属性
def logout():
    # session清除user_id
    del session['user_id']
    # 重定向到登录界面
    return redirect(url_for('cms.login'))             # 重定向(redirec)为把url变为重定向的url


# 定义个人中心的路由
@cms_bp.route("/profile/")
def profile():
    return render_template("cms/cms_profile.html")   # 模板渲染(render_template)则不会改变url,模板渲染是用模板来渲染请求的url


# 定义类视图,显示模板文件   用户登录功能实现
class LoginView(views.MethodView):
    def get(self, message=None):                                         # message=None时候不传输信息到cms_login.html页面
        return render_template("cms/cms_login.html", message=message)    # 针对post方法中同样要返回到cms_login.html页面进行代码简化
    
    # 用户登录操作验证
    def post(self):
        # 收集表单信息
        login_form = LoginForm(request.form)
        if login_form.validate():
            # 数据库验证
            email = login_form.email.data
            password = login_form.password.data
            remember = login_form.remember.data
            
            # 查询数据库中的用户信息
            user = CMS_User.query.filter_by(email=email).first()    # 邮箱唯一,用于查询验证用户
            if user and user.check_password(password):              # 验证用户和密码是否都正确
                session['user_id'] = user.id                        # 查询到用户数据时,保存session的id到浏览器
                # session['user_name'] = user.username                # 将数据库中的user.username保存到session中,在hooks.py中判断
                # session['user_email'] = user.email                  # 将数据库中的email保存到session中,方便html调用信息
                # session['user_join_time'] = user.join_time          # 将数据库中的join_time保存到session中,方便html调用信息
                
                if remember:                                        # 如果用户点击了remember选择,在浏览器中进行数据持久化
                    session.permanent = True                        # 数据持久化,默认31天,需要设置session_key在config.py中
            
                # 登录成功,跳转到后台首页
                return redirect(url_for('cms.index'))               # 在蓝图中必须加cms   跳转到index方法
            else:
                # return "邮箱或密码错误"                              # 登录出错,返回结果
                # return render_template("cms/cms_login.html", message="邮箱或密码错误")  # 登录出错,返回结果渲染到cms_login.html页面
                return self.get(message="邮箱或密码错误")             # 传参到get方法中,多加一个传输错误信息的参数到方法中
        else:
            # print(login_form.errors)                                 # forms.py中的错误信息  字典类型数据
            # print(login_form.errors.popitem())                       # forms.py中的错误信息  元祖类型数据
            # return "表单验证错误"                                     # 错误信息需要渲染到cms_login.html页面
            # return self.get(message=login_form.errors.popitem()[1][0])  # 字典类型数据信息提取
            return self.get(message=login_form.get_error())            # login_form是收集到的表单信息,信息提取放置到forms.py的父类中实现
    
    
# 修改密码的类视图验证
class ResetPwd(views.MethodView):
    def get(self):
        return render_template('cms/cms_resetpwd.html')         # 模板渲染到cms_resetpwd.html
    
    # post提交密码修改
    def post(self):
        # 先审查旧密码是否与数据库中的信息相同
        form = ResetPwdForm(request.form)
        if form.validate():
            oldpwd = form.oldpwd.data
            newpwd = form.newpwd.data
            # 对象
            user = g.cms_user
            # 将用户输入的密码进行加密检测是否与数据库中的相同
            if user.check_password(oldpwd):
                # 更新我的密码  将新密码赋值,此时的新密码已经经过验证二次密码是否一致
                user.password = newpwd         # user.password已经调用了models.py中的 @property装饰器进行密码加密
                # 数据库更新
                db.session.commit()
                # return jsonify({"code": 400, "message": "密码修改成功"})        # 代码改写为下面
                return restful.success("密码修改成功")             # 调用restful.py中定义的访问网页成功的函数
            else:
                # 当前用户输入的旧密码与数据库中的不符
                # return jsonify({"code": 400, "message": "旧密码输入错误"})
                return restful.params_error(message="旧密码输入错误")      # 参数错误
        else:
            # ajax 需要返回一个json类型的数据
            # message = form.errors.popitem()[1][0]                     # 收集错误信息
            # return jsonify({"code": 400, "message": message})         # 将数据转换成json类型
            return restful.params_error(message=form.get_error())       # 参数错误,信息的收集在forms.py的父类函数中实现  form是收集到的信息
        

# 定义修改邮箱的类视图 验证
class ResetEmail(views.MethodView):
    def get(self):
        return render_template("cms/cms_resetemail.html")      # 返回到修改邮箱页面url
    
    def post(self):
        form = ResetEmailForm(request.form)                    # 接收邮箱验证的form表单信息
        if form.validate():                                    # 验证表单信息是否通过
            email = form.email.data                            # 获取form表单中填写的邮箱地址
            
            # 查询数据库
            # CMS_User.query.filter_by(email=email).first()
            # CMS_User.query.filter(CMS_User.email == email).first()
            g.cms_user.email = email                           # 数据库中的查询在apps/cms/hooks.py文件中确定了该用户的数据库信息,用全局对象g.cms_user修改邮箱
            db.session.commit()
            return restful.success()                           # 邮箱修改成功
        else:
            return restful.params_error(form.get_error())      # form是这个类中的所有表单信息
        
        
# 发送测试邮件进行验证
@cms_bp.route("/send_email/")
def send_mail():
    message = Message('邮件发送', recipients=['[email protected]'], body='测试邮件发送')   # 主题:邮件发送;收件人:recipients;邮件内容:测试邮件发送
    mail.send(message)                   # 发送邮件
    return "邮件已发送"


# 邮件发送
class EmailCaptcha(views.MethodView):
    def get(self):                                  # 根据resetemail.js中的ajax方法来写函数,不需要post请求
        email = request.args.get('email')           # 查询email参数是否存在
        if not email:
            return restful.params_error('请传递邮箱参数')
        
        # 发送邮件,内容为一个验证码:4、6位数字英文组合
        captcha = random_captcha.get_random_captcha(4)            # 生成4位验证码
        message = Message('BBS论坛邮箱验证码', recipients=[email], body='您的验证码是:%s' % captcha)
        
        # 异常处理
        try:
            mail.send(message)
        except:
            return restful.server_error(message="服务器错误,邮件验证码未发送!")   # 发送异常,服务器错误
        
        # 验证码保存,一般有时效性,且频繁请求变化,所以保存在Redis中
        redis_captcha.redis_set(key=email, value=captcha)        # redis中都是键值对类型,存储验证码
        return restful.success("邮件验证码发送成功!")
    

# 帖子管理路由 ,需要和cms_base.js中命名的相同才可以
@cms_bp.route("/posts/")
@permission_required(CMSPersmission.POSTER)                # 传参装饰器验证不同用户不同模块权限
def posts():
    return render_template("cms/cms_posts.html")
    

# 评论管理路由
@cms_bp.route("/comments/")
@permission_required(CMSPersmission.COMMENTER)                # 传参装饰器验证不同用户不同模块权限
def comments():
    return render_template("cms/cms_comments.html")


# 板块管理路由
@cms_bp.route("/boards/")
@permission_required(CMSPersmission.BOARDER)                # 传参装饰器验证不同用户不同模块权限
def boards():
    return render_template("cms/cms_boards.html")


# 前台用户管理路由
@cms_bp.route("/fusers/")
@permission_required(CMSPersmission.FRONTUSER)                # 传参装饰器验证不同用户不同模块权限
def fuser():
    return render_template("cms/cms_fuser.html")


# 后用户管理路由
@cms_bp.route("/cusers/")
@permission_required(CMSPersmission.CMSUSER)                # 传参装饰器验证不同用户不同模块权限
def cuser():
    return render_template("cms/cms_cuser.html")


# 添加登录路由
cms_bp.add_url_rule("/login/", view_func=LoginView.as_view('login'))    # view_func 命名操作名字,"/login/"路由地址

# 类视图函数添加绑定路由  注意类视图需要修改ResetPwd.as_view('resetpwd')
cms_bp.add_url_rule("/resetpwd/", view_func=ResetPwd.as_view('resetpwd'))  # view_func 命名操作名字,/resetpwd/路由地址

# 添加修改邮箱的类视图路由绑定,路由的命名和cms_base.js中的命名要相同,否则不关联,url=/resetemail/必须要和resetemail.js中的ajax绑定的路由相同
cms_bp.add_url_rule("/resetemail/", view_func=ResetEmail.as_view('resetemail'))

# 绑定路由,路由的命名和cms_base.js中的命名要相同,必须要和resetemail.js中的ajax绑定的路由相同
cms_bp.add_url_rule("/email_captcha/", view_func=EmailCaptcha.as_view('email_captcha'))

在这里插入图片描述
同时添加不同模块管理的html页面文件,继承cms_base.html
模板文件templates/cms/cms_posts.html

<!--  继承模板文件cms/cms_base.html  简化代码 -->
{% extends 'cms/cms_base.html' %}

<!-- 页面标题 -->
{% block title %}
    帖子管理
{% endblock %}

<!--  标题  -->
{% block page_title %}
    {{self.title()}}
{% endblock %}


{% block content %}

{% endblock %}

模板文件templates/cms/cms_comments.html

<!--  继承模板文件cms/cms_base.html  简化代码 -->
{% extends 'cms/cms_base.html' %}

<!-- 页面标题 -->
{% block title %}
    评论管理
{% endblock %}

<!--  标题  -->
{% block page_title %}
    {{self.title()}}
{% endblock %}


{% block content %}

{% endblock %}

模板文件templates/cms/cms_boards.html

<!--  继承模板文件cms/cms_base.html  简化代码 -->
{% extends 'cms/cms_base.html' %}

<!-- 页面标题 -->
{% block title %}
    板块管理
{% endblock %}

<!--  标题  -->
{% block page_title %}
    {{self.title()}}
{% endblock %}


{% block content %}

{% endblock %}

模板文件templates/cms/cms_fusers.html

<!--  继承模板文件cms/cms_base.html  简化代码 -->
{% extends 'cms/cms_base.html' %}

<!-- 页面标题 -->
{% block title %}
    前台用户管理
{% endblock %}

<!--  标题  -->
{% block page_title %}
    {{self.title()}}
{% endblock %}


{% block content %}

{% endblock %}

模板文件templates/cms/cms_cusers.html

<!--  继承模板文件cms/cms_base.html  简化代码 -->
{% extends 'cms/cms_base.html' %}

<!-- 页面标题 -->
{% block title %}
    后台用户管理
{% endblock %}

<!--  标题  -->
{% block page_title %}
    {{self.title()}}
{% endblock %}


{% block content %}

{% endblock %}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章