说明
基于册本《Flask Web全栈开发实战》【黄勇·著】第9章项目实战
创建项目
代码环境:pycharm
File->New Project->Flask
安装相应的python 包
- # 用于flask在使用ORM模型操作数据库
- pip install flask-sqlalchemy
- # Python操作数据库的驱动程序
- pip install pymysql
- # 对密码加密和解密
- pip install cryptography
- # 用于将ORM模型的变更同步到数据库中
- pip install flask-migrate
复制代码 config.py文件
在根目录下,常见一个config.py的python文件,用来存放设置项。
- class BaseConfig:
- SECRET_KEY = 'linql_test'
- SQLALCHEMY_TRACK_MODIFICATIONS = False
- # 开发环境
- class DevelopmentConfig(BaseConfig):
- # 配置连接数据库
- HOSTNAME = '192.168.3.5' #服务器地址
- PORT = 3306 #默认端口号
- USERNAME = 'root'
- PASSWORD = 'root'
- DATABASE = 'pythonbbs' #数据库名
- SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
-
- # 测试环境
- class TestingConfig(BaseConfig):
- # 配置连接数据库
- HOSTNAME = '192.168.3.5' # 服务器地址
- PORT = 3306 # 默认端口号
- USERNAME = 'root'
- PASSWORD = 'root'
- DATABASE = 'pythonbbs' # 数据库名
- SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
- # 生产部署环境
- class ProductionConfig(BaseConfig):
- # 配置连接数据库
- HOSTNAME = '192.168.3.5' # 服务器地址
- PORT = 3306 # 默认端口号
- USERNAME = 'root'
- PASSWORD = 'root'
- DATABASE = 'pythonbbs' # 数据库名
- SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
复制代码 在app.py中,绑定设置
- from flask import Flask
- import config
- app = Flask(__name__)
- # 引入开发环境
- app.config.from_object(config.DevelopmentConfig)
- @app.route('/')
- def hello_world():
- return 'Hello World!'
- if __name__ == '__main__':
- app.run()
复制代码 exts.py文件
在根目录里创建exts.py文件,主要用来存放一些第三方插件的对象。
如:SQLAlchemy对象、Flask-Mail对象等。
目标,是为了防止循环引用。
- from flask_sqlalchemy import SQLAlchemy
- db = SQLAlchemy()
复制代码 回到app.py中,然后导入db变量,再通过db.init_app(app)完成初始化。
- from flask import Flask
- import config
- from exts import db
- app = Flask(__name__)
- # 引入开发环境
- app.config.from_object(config.DevelopmentConfig)
- # 初始化db
- db.init_app(app)
- @app.route('/')
- def hello_world():
- return 'Hello World!'
- if __name__ == '__main__':
- app.run()
复制代码 blueprints模块
通过蓝图来模块化,创建一个blueprints的包,用于存放蓝图模块。
在项目名称上右击,New-> ython Package
并在,blueprints下,分别创建名为:cms、front和user的python文件
在cms.py中创建蓝图对象
- from flask import Blueprint
- bp = Blueprint("cms",__name__,url_prefix="/cms")
复制代码 在front.py中创建蓝图对象
- from flask import Blueprint
- bp = Blueprint("front",__name__,url_prefix="")
复制代码 在user.py中创建蓝图对象
- from flask import Blueprint
- bp = Blueprint("user",__name__,url_prefix="/user")
复制代码 创建了蓝图对象,并指定了url前缀,因front是面向前台的,以是url为空
创建蓝图对象后,还需要在app.py中完成注册。
- from flask import Flask
- import config
- from exts import db
- from blueprints.cms import bp as cms_bp
- from blueprints.front import bp as front_bp
- from blueprints.user import bp as user_bp
- app = Flask(__name__)
- # 引入开发环境
- app.config.from_object(config.DevelopmentConfig)
- # 初始化db
- db.init_app(app)
- # 注册蓝图
- app.register_blueprint(cms_bp)
- app.register_blueprint(front_bp)
- app.register_blueprint(user_bp)
- @app.route('/')
- def hello_world():
- return 'Hello World!'
- if __name__ == '__main__':
- app.run()
复制代码 models模块
在根目录下,创建一个名为models的Python Package,然后在models下分别创建user.py和post.py,用来存放与用户和帖子相关的ORM模子。
创建用户相关模子
创建权限和角色模子
用户系统最核心的部门就是用户相关的ORM模子。
该系统的前台和后台用的是同一个用户系统,而后台系统中需要角色和权限管理。
首先,来添加权限ORM模子,在models/user.py中添加以下代码:
- from exts import db
- from datetime import datetime
- from enum import Enum
- # PermissionEnum 枚举类型
- # 存放权限类型的枚举
- class PermissionEnum(Enum):
- BOARD = "板块"
- POST = "帖子"
- COMMENT = "评论"
- FRONT_USER = "前台用户"
- CMS_USER = "后台用户"
- # PermissionModel模型
- # name 从PermissionEnum 枚举取
- class PermissionModel(db.Model):
- __tablename__="permission"
- id = db.Column(db.Integer,primary_key=True,autoincrement=True)
- name = db.Column(db.Enum(PermissionEnum),nullable=False,unique=True)
- # 中间表
- # role_id来引用role表
- # permission_id来引用permission表
- role_permission_table=db.Table(
- "role_permission_table",
- db.Column("role_id",db.Integer,db.ForeignKey("role.id")),
- db.Column("permission_id",db.Integer,db.ForeignKey("permission.id"))
- )
- class RoleModel(db.Model):
- __tablename__="role"
- # 主键id
- id = db.Column(db.Integer,primary_key=True,autoincrement=True)
- # 角色名称
- name = db.Column(db.String(100),nullable=False)
- # 角色描述
- desc = db.Column(db.String(100),nullable=False)
- # 创建时间
- create_time = db.Column(db.Datetime,default=datetime.now)
- # 关系属性(RoleModel和PermissionModel属于多对多关系)
- # role_permission_table 中间表
- permissions = db.relationship("PermissionModel",secondary=role_permission_table,backref="roles")
复制代码 在PermissionModel和RoleModel创建完成后,将模子映射到数据库中,需要借助Flask-Migrate插件
在app.py中,创建Migrate对象
- from flask import Flask
- import config
- from exts import db
- from blueprints.cms import bp as cms_bp
- from blueprints.front import bp as front_bp
- from blueprints.user import bp as user_bp
- from flask_migrate import Migrate
- app = Flask(__name__)
- # 引入开发环境
- app.config.from_object(config.DevelopmentConfig)
- # 初始化db
- db.init_app(app)
- # 注册蓝图
- app.register_blueprint(cms_bp)
- app.register_blueprint(front_bp)
- app.register_blueprint(user_bp)
- # 创建Migrate对象
- migrate = Migrate(app,db)
- @app.route('/')
- def hello_world():
- return 'Hello World!'
- if __name__ == '__main__':
- app.run()
复制代码 为了迁移时能让步伐辨认到models/user.py中的ORM模子,需要导入
- ---------------------------------------------------------------------------ModuleNotFoundError Traceback (most recent call last)Cell In[5], line 1----> 1 from models import user
- ModuleNotFoundError: No module named 'models'
复制代码 在pyCharm的Terminal中实行以下下令,以生成迁移脚本,并完成迁移脚本的实行。
- (pythonWeb) linql@localhost flaskDemo1 % flask db migrate -m "create permission and role model"
- Error: Path doesn't exist: '/Users/linql/Desktop/code_Deeplearn/flaskDemo1/migrations'. Please use the 'init' command to create a new scripts folder.
复制代码 上面运行,会报错。提示需要先“init”
- # 先执行
- flask db init
- # 再执行
- flask db migrate -m "create permission and role model"
复制代码
实行以上下令后,已经为ORM模子生成了迁移脚本。
但,此时并没有真正同步到数据库中,还需要实行以下下令才会实现同步到数据库中。
创建权限和角色
Flask 项目 如何创建下令
在flask安装时,默认会安装click库
click库,主要作用是用来实现下令,Flask已经针对click库进行了集成,通过app.cli即可访问到click对象。
下面是一个简单的下令测试
- from flask import Flaskimport configfrom exts import dbfrom blueprints.cms import bp as cms_bpfrom blueprints.front import bp as front_bpfrom blueprints.user import bp as user_bpfrom flask_migrate import Migratefrom models import user
- import clickapp = Flask(__name__)# 引入开发环境app.config.from_object(config.DevelopmentConfig)# 初始化dbdb.init_app(app)# 注册蓝图app.register_blueprint(cms_bp)app.register_blueprint(front_bp)app.register_blueprint(user_bp)# 创建Migrate对象migrate = Migrate(app,db)@app.route('/')def hello_world(): return 'Hello World!'@app.cli.command("my-command")def my_command(): click.echo("这是我的自定义下令!")if __name__ == '__main__': app.run()
复制代码 通过装饰器@app.cli.command,将my_command函数添加到下令中,而且指定下令的名称为my-command。
然后,再在pycharm的terminal中输入以下下令。

在app.py中,实现一个名叫create-permission的下令。
针对每个模块分别添加一个权限。
- from flask import Flask
- import config
- from exts import db
- from blueprints.cms import bp as cms_bp
- from blueprints.front import bp as front_bp
- from blueprints.user import bp as user_bp
- from flask_migrate import Migrate
- from models.user import PermissionEnum,PermissionModel,RoleModel
- import click
- app = Flask(__name__)
- # 引入开发环境
- app.config.from_object(config.DevelopmentConfig)
- # 初始化db
- db.init_app(app)
- # 注册蓝图
- app.register_blueprint(cms_bp)
- app.register_blueprint(front_bp)
- app.register_blueprint(user_bp)
- # 创建Migrate对象
- migrate = Migrate(app,db)
- @app.route('/')
- def hello_world():
- return 'Hello World!'
- # 测试命令
- @app.cli.command("my-command")
- def my_command():
- click.echo("这是我的自定义命令!")
- # 创建create-permission命令
- @app.cli.command("create-permission")
- def create_permission():
- for permission_name in dir(PermissionEnum):
- if permission_name.startswith("__"):
- continue
- permission = PermissionModel(name=getattr(PermissionEnum,permission_name))
- db.session.add(permission)
- db.session.commit()
- click.echo("权限添加成功")
- if __name__ == '__main__':
- app.run()
复制代码 然后,再在pycharm的terminal中输入以下下令。
权限创建乐成后,再添加角色。
创建3个角色,分别为:稽查、运营、管理员。
角色名,稽查,权限:帖子、评论
角色名,运营,权限:板块、帖子、评论、前台用户
角色名,管理员,权限:板块、帖子、评论、前台用户、后台用户
创建角色,同创建权限一样操纵,下面只给出代码,实行步骤划一
- from flask import Flask
- import config
- from exts import db
- from blueprints.cms import bp as cms_bp
- from blueprints.front import bp as front_bp
- from blueprints.user import bp as user_bp
- from flask_migrate import Migrate
- from models.user import PermissionEnum, PermissionModel, RoleModel
- import click
- app = Flask(__name__)
- # 引入开发环境
- app.config.from_object(config.DevelopmentConfig)
- # 初始化db
- db.init_app(app)
- # 注册蓝图
- app.register_blueprint(cms_bp)
- app.register_blueprint(front_bp)
- app.register_blueprint(user_bp)
- # 创建Migrate对象
- migrate = Migrate(app, db)
- @app.route('/')
- def hello_world():
- return 'Hello World!'
- # 测试命令
- @app.cli.command("my-command")
- def my_command():
- click.echo("这是我的自定义命令!")
- # 创建添加权限的命令create-permission
- @app.cli.command("create-permission")
- def create_permission():
- for permission_name in dir(PermissionEnum):
- if permission_name.startswith("__"):
- continue
- permission = PermissionModel(name=getattr(PermissionEnum, permission_name))
- db.session.add(permission)
- db.session.commit()
- click.echo("权限添加成功")
- # 创建添加角色的命令create-role
- @app.cli.command("create-role")
- def create_role():
- # 稽查
- inspector = RoleModel(name="稽查", desc="负责审核帖子和评论是否合法合规")
- inspector.permissions = PermissionModel.query.filter(
- PermissionModel.name.in_([PermissionEnum.POST, PermissionEnum.COMMENT])).all()
- # 运营
- operator = RoleModel(name="运营", desc="负责网站持续正常运营!")
- operator.permissions = PermissionModel.query.filter(PermissionModel.name.in_(
- [PermissionEnum.POST, PermissionEnum.COMMENT, PermissionEnum.BOARD, PermissionEnum.FRONT_USER,
- PermissionEnum.CMS_USER])).all()
- # 管理员
- administrator = RoleModel(name="管理员", desc="负责整个网站所有工作!")
- administrator.permissions = PermissionModel.query.all()
- db.session.add_all([inspector,operator,administrator])
- db.session.commit()
- click.echo("角色创建成功!")
- if __name__ == '__main__':
- app.run()
复制代码- # 在pycharm中的terminal中运行
- flask create-role
复制代码
对app.py进行瘦身,将command部门的代码放到commands.py里
- # commands.py
- from models.user import PermissionEnum, PermissionModel, RoleModel
- import click
- from exts import db
- # 测试命令
- # @app.cli.command("my-command")
- def my_command():
- click.echo("这是我的自定义命令!")
- # 创建添加权限的命令create-permission
- # @app.cli.command("create-permission")
- def create_permission():
- for permission_name in dir(PermissionEnum):
- if permission_name.startswith("__"):
- continue
- permission = PermissionModel(name=getattr(PermissionEnum, permission_name))
- db.session.add(permission)
- db.session.commit()
- click.echo("权限添加成功")
- # 创建添加角色的命令create-role
- # @app.cli.command("create-role")
- def create_role():
- # 稽查
- inspector = RoleModel(name="稽查", desc="负责审核帖子和评论是否合法合规")
- inspector.permissions = PermissionModel.query.filter(
- PermissionModel.name.in_([PermissionEnum.POST, PermissionEnum.COMMENT])).all()
- # 运营
- operator = RoleModel(name="运营", desc="负责网站持续正常运营!")
- operator.permissions = PermissionModel.query.filter(PermissionModel.name.in_(
- [PermissionEnum.POST, PermissionEnum.COMMENT, PermissionEnum.BOARD, PermissionEnum.FRONT_USER,
- PermissionEnum.CMS_USER])).all()
- # 管理员
- administrator = RoleModel(name="管理员", desc="负责整个网站所有工作!")
- administrator.permissions = PermissionModel.query.all()
- db.session.add_all([inspector,operator,administrator])
- db.session.commit()
- click.echo("角色创建成功!")
复制代码- # app.py文件
- from flask import Flask
- import config
- from exts import db
- from blueprints.cms import bp as cms_bp
- from blueprints.front import bp as front_bp
- from blueprints.user import bp as user_bp
- from flask_migrate import Migrate
- # 引入了命令
- import commands
- app = Flask(__name__)
- # 引入开发环境
- app.config.from_object(config.DevelopmentConfig)
- # 初始化db
- db.init_app(app)
- # 注册蓝图
- app.register_blueprint(cms_bp)
- app.register_blueprint(front_bp)
- app.register_blueprint(user_bp)
- # 创建Migrate对象
- migrate = Migrate(app, db)
- # 添加命令
- app.cli.command("create-permission")(commands.create_permission)
- app.cli.command("create-role")(commands.create_role)
- app.cli.command("my-command")(commands.my_command) #瘦身后,再次测试
- @app.route('/')
- def hello_world():
- return 'Hello World!'
- if __name__ == '__main__':
- app.run()
复制代码
创建用户模子
UUID(universally unique identfier)
UUID的长度为32字符,再加上4个横线,总共有36个字符。
引入python第三方库,shortuuid
shortuuid 会对原始UUID进行base57 编码,然后删除相似的字符,如:I,1,L,o和0,末了生成默认22位长度的字符串。
- # 安装shortuuid库
- pip install shortuuid
复制代码 重构UserModel
- from exts import db
- from datetime import datetime
- from enum import Enum
- # 引入shortuuid
- from shortuuid import uuid
- # 引入加密和验证
- from werkzeug.security import generate_password_hash,check_password_hash
- # PermissionEnum 枚举类型
- # 存放权限类型的枚举
- class PermissionEnum(Enum):
- BOARD = "板块"
- POST = "帖子"
- COMMENT = "评论"
- FRONT_USER = "前台用户"
- CMS_USER = "后台用户"
- # PermissionModel模型
- # name 从PermissionEnum 枚举取
- class PermissionModel(db.Model):
- __tablename__ = "permission"
- id = db.Column(db.Integer, primary_key=True, autoincrement=True)
- name = db.Column(db.Enum(PermissionEnum), nullable=False, unique=True)
- # 中间表
- # role_id来引用role表
- # permission_id来引用permission表
- role_permission_table = db.Table(
- "role_permission_table",
- db.Column("role_id", db.Integer, db.ForeignKey("role.id")),
- db.Column("permission_id", db.Integer, db.ForeignKey("permission.id"))
- )
- class RoleModel(db.Model):
- __tablename__ = "role"
- # 主键id
- id = db.Column(db.Integer, primary_key=True, autoincrement=True)
- # 角色名称
- name = db.Column(db.String(100), nullable=False)
- # 角色描述
- desc = db.Column(db.String(100), nullable=False)
- # 创建时间
- create_time = db.Column(db.DateTime, default=datetime.now)
- # 关系属性(RoleModel和PermissionModel属于多对多关系)
- # role_permission_table 中间表
- permissions = db.relationship("PermissionModel", secondary=role_permission_table, backref="roles")
- class UserModel(db.Model):
- __tablename__ = "user"
- # 主键
- id = db.Column(db.String(100), primary_key=True, default=uuid)
- # 用户名,不能为空,且值唯一
- username = db.Column(db.String(50), nullable=False, unique=True)
- # 密码,最大长度200,应该先加密再存储
- _password = db.Column(db.String(200), nullable=False)
- # 邮箱,不能为空,且值唯一
- email = db.Column(db.String(50), nullable=False, unique=True)
- # 头像,存储图片在服务器中保存的路径,可以为空
- avatar = db.Column(db.String(100))
- # 签名,可以为空
- signature = db.Column(db.String(100))
- # 加入时间,第一次存储,默认当前时间
- join_time = db.Column(db.DateTime, default=datetime.now)
- # 是否员工,只有员工才能进入后台系统,默认为False
- is_staff = db.Column(db.Boolean, default=False)
- # 是否可用,默认情况下是可用
- is_active = db.Column(db.Boolean, default=True)
- is_admin = db.Column(db.Boolean,default=False)
- # 外键
- # 角色外键,引用role表的id字段
- role_id = db.Column(db.Integer,db.ForeignKey("role.id"))
- # 关系属性,引用RoleModel
- role = db.relationship("RoleModel",backref="users")
- def __init__(self,*args,**kwargs):
- if "password" in kwargs:
- self.password = kwargs.get("password")
- kwargs.pop("password")
- super(UserModel,self).__init__(*args,**kwargs)
- @property
- def password(self):
- return self._password
- @password.setter
- def password(self,raw_password):
- self._password=generate_password_hash(raw_password)
- def check_password(self,raw_password):
- result = check_password_hash(self.password,raw_password)
- return result
- def has_permission(self,permission):
- return permission in [permission.name for permisson in self.role.permissions]
复制代码- # 在terminal中运行
- flask db migrate -m "create user model"
- flask db upgrade
复制代码
创建测试用户
为了方便后续开发,按照角色个数创建3个员工账户。
在commands.py文件中,添加以下代码
- # 添加测试3个角色的测试用户
- def create_test_user():
- admin_role = RoleModel.query.filter_by(name="管理员").first()
- zhangsan = UserModel(username ="张三",email="zhangsan@zlkt.net",password="111111",is_staff=True,role=admin_role)
- operator_role = RoleModel.query.filter_by(name="运营").first()
- lisi = UserModel(username ="李四",email="lisi@zlkt.net",password="111111",is_staff=True,role=operator_role)
- inspector_role = RoleModel.query.filter_by(name="稽查").first()
- wangwu = UserModel(username="王五", email="wangwu@zlkt.net", password="111111", is_staff=True, role=inspector_role)
- db.session.add_all([zhangsan,lisi,wangwu])
- db.session.commit()
- click.echo("测试用户添加成功!")
复制代码 在app.py中添加,以下代码:
- # 添加命令(增加3个角色测试用户)
- app.cli.command("create-test-user")(commands.create_test_user)
复制代码 在pycharm的terminal中,运行下令
创建管理员
在commands.py中添加以下下令:
- # 创建管理员
- @click.option("--username",'-u')
- @click.option("--email",'-e')
- @click.option("--password",'-p')
- def create_admin(username,email,password):
- admin_role = RoleModel.query.filter_by(name="管理员").first()
- admin_user = UserModel(username=username,email=email,password=password,is_staff=True,role = admin_role)
- db.session.add(admin_user)
- db.session.commit()
- click.echo("管理员创建成功")
复制代码 其中,通过@click.option装饰器添加了3个参数。
以后在下令行中即可使用–username,–email,–password将用户名、邮箱、密码看成参数传递到函数中。
注册
渲染注册模版
因,未直接下载册本自带的源码,只能手动自行编辑。
1、在根目录的static文件下,创建一个文件夹,名为:front
2、在front文件夹下,创建一个文件夹,名为:css
3、在css文件夹下,创建2个文件,分别为:base.css、sign.css
1、再在根目录的templates文件夹下,创建一个文件夹,名为:front
2、在front文件夹下,创建2个文件,分别为:base.html、register.html
- # base.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <scrip src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></scrip>
- <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css">
- <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.min.css"></script>
- <link rel="stylesheet" href="{{ url_for('static',filename='front/css/base.css') }}">
- <title>{% block title %}{% endblock %}</title>
- {% block head %}{% endblock %}
- </head>
- <body>
- <nav class="navbar navbar-expand-lg navbar-light bg-light">
- <a href="#" class="navbar-brand">知了Python论坛</a>
- <button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
- aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
- <span class="navbar-toggle-icon"></span>
- </button>
- <div class="collapse navbar-collapse" id="navbarSupportedContent">
- <ul class="navbar-nav mr-auto">
- <li class="nav-item active">
- <a href="/" class="nav-link">首页 <span class="sr-only">(current)</span></a>
- </li>
- </ul>
- <form class="form-inline my-lg-0">
- <input class="form-control mr-sm-2" type="search" placeholder="请输入关键字" aria-label="Search">
- <button class="btn btn-outline-success my-2 my-sm-0" type="submit">搜索</button>
- </form>
- <ul class="navbar-nav ml-4">
- <li class="nav-item">
- <a href="#" class="nav-link">登录</a>
- </li>
- <li class="nav-link">
- <a href="#" class="nav-link">注册</a>
- </li>
- </ul>
- </div>
- </nav>
- <div class="main-container">
- {% block body %}{% endblock %}
- </div>
- </body>
- </html>
复制代码 base.html文件是全部前台页面的父模版
jquery.min.js:3.6.0版本的JQuery文件。JQuery文件可以快速寻找元素,发送AJAX请求
bootstrap.min.css:4.6.0 版本的bootstrap样式文件。可以快速构建网页界面。
bootstrap.min.js:4.6.0 版本的bootstrap JavaScript文件。BootStrap中的一些组件运行需要通过bootstrap.min.js来实现。
- # register.html
- {% extends 'front/base.html' %}
- {% block title %}
- 知了课堂注册
- {% endblock %}
- {% block head %}
- <link rel="stylesheet" href="{{ url_for('static',filename='front/css/sign.css') }}">
- {% endblock %}
- {% block body %}
- <h1 class="page-title">注册</h1>
- <div class="sign-box">
- <form action="" id="register-form">
- <div class="form-group">
- <div class="input-group">
- <input type="text" class="form-control" name="email" placeholder="邮箱">
- <div class="input-group-append">
- <button id="captcha-btn" class="btn btn-outline-secondary">发送验证码</button>
- </div>
- </div>
- </div>
- <div class="form-group">
- <input type="text" class="form-control" name="captcha" placeholder="邮箱验证码">
- </div>
- <div class="form-group">
- <input type="text" class="form-control" name="username" placeholder="用户名">
- </div>
- <div class="form-group">
- <input type="password" class="form-control" name="password" placeholder="密码">
- </div>
- <div class="form-group">
- <input type="password" class="form-control" name="confirm_password" placeholder="确认密码">
- </div>
- <div class="form-group">
- <a href="#" class="signup-link">返回登录</a>
- <a href="#" class="resetpwd-link" style="float: right">找回密码</a>
- </div>
- </form>
- </div>
- {% endblock %}
复制代码 在blueprints/user.py中,添加以下代码:
- from flask import Blueprint,render_template
- bp = Blueprint("user",__name__,url_prefix="/user")
- @bp.route("/register/")
- def register():
- return render_template("front/register.html")
复制代码 使用Flask-Mail发送邮箱验证码
安装Flask-Mail
在Pycharm的Terminal,输入并实行:
pip install flask-mail
设置邮箱参数
- # 以QQ邮箱为例,其QQ邮箱,POP3/IMAP/SMTP/Exchange/CardDAV授权,请查阅其他资料完成。
复制代码 开启个人邮箱的SMTP服务后,在项目中,打开config.py文件,在DevelopmentConfig中添加以下代码。
- # 开发环境
- class DevelopmentConfig(BaseConfig):
- # 配置连接数据库
- HOSTNAME = '192.168.3.5' # 服务器地址
- PORT = 3306 # 默认端口号
- USERNAME = 'root'
- PASSWORD = 'root'
- DATABASE = 'pythonbbs' # 数据库名
- SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
- # 邮箱配置
- MAIL_SERVER = "smtp.qq.com"
- MAIL_USE_SSL = True
- MAIL_USE_TLS = False
- MAIL_PORT = 465
- MAIL_USERNAME = "**@qq.com" # 发送者邮箱
- MAIL_PASSWORD = "****" # SMTP授权码
- MAIL_DEFAULT_SENDER = "**@qq.com" #默认发送邮箱
复制代码 发送邮件
在项目中,打开exts.py中,创建1个mail对象
- from flask_sqlalchemy import SQLAlchemy
- from flask_mail import Mail
- db = SQLAlchemy()
- # 创建1个mail对象
- mail = Mail()
复制代码 回到,app.py文件,从exts.py中导入mail变量,并进行初始化。
- from flask import Flaskimport configfrom exts import db,mailfrom blueprints.cms import bp as cms_bpfrom blueprints.front import bp as front_bpfrom blueprints.user import bp as user_bpfrom flask_migrate import Migrate# 引入了下令import commandsapp = Flask(__name__)# 引入开发环境app.config.from_object(config.DevelopmentConfig)# 初始化dbdb.init_app(app)# 初始化mailmail.init_app(app)# 注册蓝图app.register_blueprint(cms_bp)app.register_blueprint(front_bp)app.register_blueprint(user_bp)# 创建Migrate对象migrate = Migrate(app, db)# 添加下令app.cli.command("create-permission")(commands.create_permission)app.cli.command("create-role")(commands.create_role)app.cli.command("my-command")(commands.my_command) #瘦身后,再次测试# 添加命令(增加3个角色测试用户)
- app.cli.command("create-test-user")(commands.create_test_user)
- # 创建管理员下令app.cli.command("create-admin")(commands.create_admin)@app.route('/')def hello_world(): return 'Hello World!'if __name__ == '__main__': app.run()
复制代码 完成Flask-Mail对象的初始化,后续就可使用mail变量发送邮件了。
接着,在blueprints/user.py中输入以下代码:
- from flask import Blueprint, render_template
- # 发送邮箱,引入的2个包
- from flask_mail import Message
- from exts import mail
- bp = Blueprint("user", __name__, url_prefix="/user")
- @bp.route("/register/")
- def register():
- return render_template("front/register.html")
- @bp.route('/mail/captcha/')
- def mail_captcha():
- # recipients,填写接收者的邮箱地址
- # 可以写多个,用,逗号隔开。
- message = Message(subject="我是邮件主题", recipients=['***@outlook.com'], body="我是邮件内容")
- mail.send(message)
- return "success"
复制代码 运行,输入URL:http://127.0.0.1:5000/user/mail/captcha/

上面是测试,下面是针对项目验证码的代码:
- from flask import Blueprint, render_template, request
- import random
- # 发送邮箱,引入的2个包
- from flask_mail import Message
- from exts import mail
- bp = Blueprint("user", __name__, url_prefix="/user")
- @bp.route("/register/")
- def register():
- return render_template("front/register.html")
- @bp.route('/mail/captcha/')
- def mail_catpcha():
- email = request.args.get("mail")
- digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
- captcha = "".join(random.sample(digits, 4))
- body=f"[知了Python论坛]您的注册验证码是:{captcha},请勿告诉别人!"
- message = Message(subject="我是邮箱主题",recipients=[email],body=body)
- mail.send(message)
- return "Succes"
复制代码 使用Flask-Caching和Redis缓存验证码
要在Python中使用Redis,首先需要安装Redis包。
打开pyCharm中的terminal,输入以下下令:
pip install redis
在Flask中使用Redis,可以借助第三方插件Flask-Caching实现。
打开Pycharm中的Terminal,输入以下下令:
pip install flask-caching
回到项目中,打开config.py文件
在DevelopmentConfig中添加Flask-Caching的设置信息,如下所示:
- # 开发环境
- class DevelopmentConfig(BaseConfig):
- # 配置连接数据库
- HOSTNAME = '192.168.3.5' # 服务器地址
- PORT = 3306 # 默认端口号
- USERNAME = 'root'
- PASSWORD = 'root'
- DATABASE = 'pythonbbs' # 数据库名
- SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
- # 邮箱配置
- MAIL_SERVER = "smtp.qq.com"
- MAIL_USE_SSL = True
- MAIL_USE_TLS = False
- MAIL_PORT = 465
- MAIL_USERNAME = "**@qq.com" # 发送者邮箱
- MAIL_PASSWORD = "****" # SMTP授权码
- MAIL_DEFAULT_SENDER = "**@qq.com" #默认发送邮箱
- # 缓存设置 CACHE_TYPE = "RedisCache" CACHE_REDIS_HOST = "127.0.0.1" CACHE_REDIS_PORT = 6379
复制代码 在项目中,打开exts.py文件,然后输入以下代码:
- from flask_sqlalchemy import SQLAlchemy
- from flask_mail import Mail
- from flask_caching import Cache
- db = SQLAlchemy()
- # 创建1个mail对象
- mail = Mail()
- # 创建1个Flask-Caching对象
- cache = Cache()
复制代码 再回到app.py文件中,从exts.py文件中导入cache变量,而且进行初始化,代码如下:
- from flask import Flaskimport configfrom exts import db,mail,cachefrom blueprints.cms import bp as cms_bpfrom blueprints.front import bp as front_bpfrom blueprints.user import bp as user_bpfrom flask_migrate import Migrate# 引入了下令import commandsapp = Flask(__name__)# 引入开发环境app.config.from_object(config.DevelopmentConfig)# 初始化dbdb.init_app(app)# 初始化mailmail.init_app(app)# 初始化cachecache.init_app(app)# 注册蓝图app.register_blueprint(cms_bp)app.register_blueprint(front_bp)app.register_blueprint(user_bp)# 创建Migrate对象migrate = Migrate(app, db)# 添加下令app.cli.command("create-permission")(commands.create_permission)app.cli.command("create-role")(commands.create_role)app.cli.command("my-command")(commands.my_command) #瘦身后,再次测试# 添加命令(增加3个角色测试用户)
- app.cli.command("create-test-user")(commands.create_test_user)
- # 创建管理员下令app.cli.command("create-admin")(commands.create_admin)@app.route('/')def hello_world(): return 'Hello World!'if __name__ == '__main__': app.run()
复制代码 Flask-Caching初始化完成后,就可以用它来缓存数据了。
回到blueprints/user.py文件中
先从exts.py文件中导入cache对象,然后将email.captcha代码修改如下:
- from flask import Blueprint, render_template, request
- import random
- # 发送邮箱,引入的2个包
- from flask_mail import Message
- from exts import mail, cache
- bp = Blueprint("user", __name__, url_prefix="/user")
- @bp.route("/register/")
- def register():
- return render_template("front/register.html")
- # # 测试使用
- # @bp.route('/mail/captcha/')
- # def mail_captcha():
- # message = Message(subject="我是邮件主题", recipients=['*****@outlook.com'], body="我是邮件内容")
- # mail.send(message)
- # return "success"
- @bp.route('/mail/captcha/')
- def mail_catpcha():
- email = request.args.get("mail")
- digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
- captcha = "".join(random.sample(digits, 4))
- body = f"[知了Python论坛]您的注册验证码是:{captcha},请勿告诉别人!"
- message = Message(subject="我是邮箱主题", recipients=[email], body=body)
- mail.send(message)
- cache.set(email, captcha, timeout=100)
- return "Succes"
复制代码 使用Celery 发送邮件
上述,可以实现发送邮件,但体验感欠好
1、发送邮件需要发起网络请求,必须要等邮件发送乐成后,欣赏器才能收到响应,用户等候长。
2、发送邮件这种耗时操纵,会导致线程被长时间占用,从而无法服务其他请求。
通过异步方式来解决。
Celery是一个任务调度框架,由纯python开发,有5大模块:
1、Task(任务)
2、Broker(中间人)
3、Celery Beat(调度器)
4、Worker(消费者)
5、Backend(存储)
Celery是一个第三方Python库,在Pycharm的terminal中输入以下下令来安装:
pip install celery
首先在config.py下的 DevelopmentConfig中添加Broker和Backend设置信息,代码如下:
- # 开发环境
- class DevelopmentConfig(BaseConfig):
- # 配置连接数据库
- HOSTNAME = '192.168.3.5' # 服务器地址
- PORT = 3306 # 默认端口号
- USERNAME = 'root'
- PASSWORD = 'root'
- DATABASE = 'pythonbbs' # 数据库名
- SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
- # 邮箱配置
- MAIL_SERVER = "smtp.qq.com"
- MAIL_USE_SSL = True
- MAIL_USE_TLS = False
- MAIL_PORT = 465
- MAIL_USERNAME = "**@qq.com" # 发送者邮箱
- MAIL_PASSWORD = "****" # SMTP授权码
- MAIL_DEFAULT_SENDER = "**@qq.com" #默认发送邮箱
- # 缓存设置 CACHE_TYPE = "RedisCache" CACHE_REDIS_HOST = "127.0.0.1" CACHE_REDIS_PORT = 6379 # Celery设置 CELERY_BROKER_URL = "redis://127.0.0.1:6379/0" CELERY_RESULT_BACKEND = "redis://127.0.0.1:6379/0"
复制代码 在根目录下,创建一个bbs_celery.py文件,然后输入以下代码:
- from flask_mail import Message
- from exts import mail
- from celery import Celery
- # 定义任务函数
- def send_mail(recipient, subject, body):
- message = Message(subject=subject, recipients=[recipient], body=body)
- mail.send(message)
- print("发送成功")
- # 创建Celery对象
- def make_celery(app):
- celery = Celery(app.import_name, backend=app.config['CELERY_RESULT_BACKEND'],
- broker=app.config['CELERY_BROKER_URL'])
- TaskBase = celery.Task
- class ContextTask(TaskBase):
- abstract = True
- def __call__(self, *args, **kwargs):
- with app.app_context():
- return TaskBase.__call__(self, *args, **kwargs)
- celery.Task = ContextTask
- app.celery = celery
- # 添加任务
- celery.task(name="send_mail")(send_mail)
- return celery
复制代码 在app.py中导入make_celery函数,并创建1个Celery对象
- from flask import Flaskimport configfrom exts import db,mail,cachefrom blueprints.cms import bp as cms_bpfrom blueprints.front import bp as front_bpfrom blueprints.user import bp as user_bpfrom flask_migrate import Migrate# 引入了下令import commands# 导入make_celery函数,并创建1个Celery对象from bbs_celery import make_celeryapp = Flask(__name__)# 引入开发环境app.config.from_object(config.DevelopmentConfig)# 初始化dbdb.init_app(app)# 初始化mailmail.init_app(app)# 初始化cachecache.init_app(app)# 构建celerycelery = make_celery(app)# 注册蓝图app.register_blueprint(cms_bp)app.register_blueprint(front_bp)app.register_blueprint(user_bp)# 创建Migrate对象migrate = Migrate(app, db)# 添加下令app.cli.command("create-permission")(commands.create_permission)app.cli.command("create-role")(commands.create_role)app.cli.command("my-command")(commands.my_command) #瘦身后,再次测试# 添加命令(增加3个角色测试用户)
- app.cli.command("create-test-user")(commands.create_test_user)
- # 创建管理员下令app.cli.command("create-admin")(commands.create_admin)@app.route('/')def hello_world(): return 'Hello World!'if __name__ == '__main__': app.run()
复制代码 回到blueprints/user.py的email_captcha视图函数中,把之前的发送邮件代码删除,改成celery任务的方式发送。
- from flask import Blueprint, render_template, request, current_app
- import random
- # 发送邮箱,引入的2个包
- from flask_mail import Message
- from exts import mail, cache
- bp = Blueprint("user", __name__, url_prefix="/user")
- @bp.route("/register/")
- def register():
- return render_template("front/register.html")
- # @bp.route('/mail/captcha/')
- # def mail_catpcha():
- # email = request.args.get("mail")
- # digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
- # captcha = "".join(random.sample(digits, 4))
- # body = f"[知了Python论坛]您的注册验证码是:{captcha},请勿告诉别人!"
- # message = Message(subject="我是邮箱主题", recipients=[email], body=body)
- # mail.send(message)
- # cache.set(email, captcha, timeout=100)
- # return "Succes"
- @bp.route('/mail/captcha/')
- def mail_catpcha():
- email = request.args.get("mail")
- digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
- captcha = "".join(random.sample(digits, 4))
- subject = "[知了Python论坛]注册验证码"
- body = f"[知了Python论坛]您的注册验证码是:{captcha},请勿告诉别人!"
- current_app.celery.send_task("send_mail", (email, subject, body))
- cache.set(email, captcha, timeout=100)
- return "Succes"
复制代码 运行Celery,需要安装别的一个第三方python库:
pip install gevent
在pycharm的terminal中,输入以下下令,启动cerely:
celery -A app.celery worker -l info

设置Redis
Mac电脑设置Redis参考:参考链接
- 【记录下这里关闭Redis报错】Mac上使用Redis无法写入快照或者
- Error trying to save the DB, can‘t exit.
复制代码 Mac上使用Redis无法写入快照大概 Error trying to save the DB, can‘t exit.
参考:https://blog.csdn.net/m0_53370288/article/details/117416009
安装Redis Desktop Manager
下载地址:https://www.macyy.cn/archives/1343#J_DLIPPCont
安装后,打开,并毗连;
RESTful API
RESTful 也叫做REST(representational state transfer,表现层状态转换)
在项目根目录下,创建1个名叫:utils的包,用于存放一些工具类模块。
接着,在utils包下,创建1个restful.py的文件,代码如下:
- from flask import jsonify
- class HttpCode(object):
- # 响应正常
- ok = 200
- # 登录错误
- unloginerror = 401
- # 权限错误
- permissionerror = 403
- # 客户端参数错误
- paramserror = 400
- # 服务器错误
- servererror = 500
- def _restful_result(code, message, data):
- return jsonify(({"message": message or "", "data": data or {}})), code
- def ok(message=None, data=None):
- return _restful_result(code=HttpCode.ok, message=message, data=data)
- def unlogin_error(message="没有登录!"):
- return _restful_result(code=HttpCode.unloginerror, message=message, data=None)
- def permission_error(message="没有权限!"):
- return _restful_result(code=HttpCode.permissionerror, message=message, data=None)
- def params_error(message="参数错误!"):
- return _restful_result(code=HttpCode.paramserror, message=message, data=None)
- def server_error(message="服务器错误!"):
- return _restful_result(code=HttpCode.servererror, message=message or "服务器内部错误", data=None)
复制代码 将blueprints/user.py中的email_captcha的代码修改如下:
- @bp.route('/mail/captcha/')
- def mail_catpcha():
- try:
- email = request.args.get("mail")
- digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
- captcha = "".join(random.sample(digits, 4))
- subject = "[知了Python论坛]注册验证码"
- body = f"[知了Python论坛]您的注册验证码是:{captcha},请勿告诉别人!"
- current_app.celery.send_task("send_mail", (email, subject, body))
- cache.set(email, captcha, timeout=100)
- return restful.ok()
- except Exception as e:
- print(e)
- return restful.server_error()
复制代码 CSRF保护
开启CSRF保护需要使用flask-wtf中的CSRFProtect
首先,在pycharm的Terminal下输入安装flask-wtf下令:
回到app.py中,添加以下代码:
- from flask import Flask import configfrom exts import db,mail,cachefrom blueprints.cms import bp as cms_bpfrom blueprints.front import bp as front_bpfrom blueprints.user import bp as user_bpfrom flask_migrate import Migrate# 引入了下令import commands# 导入make_celery函数,并创建1个Celery对象from bbs_celery import make_celery# 导入CSRFfrom flask_wtf import CSRFProtectapp = Flask(__name__)# 引入开发环境app.config.from_object(config.DevelopmentConfig)# 初始化dbdb.init_app(app)# 初始化mailmail.init_app(app)# 初始化cachecache.init_app(app)# 构建celerycelery = make_celery(app)# CSRF保护CSRFProtect(app)# 注册蓝图app.register_blueprint(cms_bp)app.register_blueprint(front_bp)app.register_blueprint(user_bp)# 创建Migrate对象migrate = Migrate(app, db)# 添加下令app.cli.command("create-permission")(commands.create_permission)app.cli.command("create-role")(commands.create_role)app.cli.command("my-command")(commands.my_command) #瘦身后,再次测试# 添加命令(增加3个角色测试用户)
- app.cli.command("create-test-user")(commands.create_test_user)
- # 创建管理员下令app.cli.command("create-admin")(commands.create_admin)@app.route('/')def hello_world(): return 'Hello World!'if __name__ == '__main__': app.run()
复制代码- ---------------------------------------------------------------------------
- ModuleNotFoundError Traceback (most recent call last)
- Cell In[9], line 2
- 1 from flask import Flask
- ----> 2 import config
- 3 from exts import db,mail,cache
- 5 from blueprints.cms import bp as cms_bp
- ModuleNotFoundError: No module named 'config'
复制代码 在templates/front/register.html的form标签下添加以下代码:
- <input type="hidden" name="crsf_token" value="{{ csrf_token() }}">
复制代码 使用AJAX获取邮箱验证码
在static/common下,创建1个名为:zlajax.js文件,如许每次发送非GET请求都不需要手动设置csrf_token了。具体代码如下:
- var zlajax = {
- 'get': function (args) {
- args['method'] = "get"
- return this.ajax(args);
- },
- 'post': function (args) {
- args['method'] = "post"
- return this.ajax(args);
- },
- 'put': function (args) {
- args['method'] = "put"
- return this.ajax(args);
- },
- 'delete': function (args) {
- args['method'] = "delete"
- return this.ajax(args);
- },
- 'ajax': function (args) {
- this._ajaxSetup();
- return $.ajax(args);
- },
- '_ajaxSetup': function () {
- $.ajaxSetup({
- 'beforeSend': function (xhr, settings) {
- if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
- var csrftoken = $('meta[name=csrf-token]').attr('content');
- xhr.setRequestHeader("X-CSRFToken", csrftoken)
- }
- }
- }
- );
- }
- };
复制代码 将zlajax.js文件放到templates/front/base.html的head标签里,如许后面全部的页面都能使用这个文件里。代码如下:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <scrip src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></scrip>
- <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css">
- <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.min.css"></script>
- <script src="{{ url_for('static',filename='common/zlajax.js') }}"></script>
- <link rel="stylesheet" href="{{ url_for('static',filename='front/css/base.css') }}">
- <title>{% block title %}{% endblock %}</title>
- {% block head %}{% endblock %}
- </head>
- <body>
- <nav class="navbar navbar-expand-lg navbar-light bg-light">
- <a href="#" class="navbar-brand">知了Python论坛</a>
- <button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
- aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
- <span class="navbar-toggle-icon"></span>
- </button>
- <div class="collapse navbar-collapse" id="navbarSupportedContent">
- <ul class="navbar-nav mr-auto">
- <li class="nav-item active">
- <a href="/" class="nav-link">首页 <span class="sr-only">(current)</span></a>
- </li>
- </ul>
- <form class="form-inline my-lg-0">
- <input class="form-control mr-sm-2" type="search" placeholder="请输入关键字" aria-label="Search">
- <button class="btn btn-outline-success my-2 my-sm-0" type="submit">搜索</button>
- </form>
- <ul class="navbar-nav ml-4">
- <li class="nav-item">
- <a href="#" class="nav-link">登录</a>
- </li>
- <li class="nav-link">
- <a href="#" class="nav-link">注册</a>
- </li>
- </ul>
- </div>
- </nav>
- <div class="main-container">
- {% block body %}{% endblock %}
- </div>
- </body>
- </html>
复制代码- 因为zlajax.js依赖JQuery,所以必须把zlajax.js放到jQuery文件后面。</br>
- 在static/front/js下创建1个register.js文件,用于绑定“发送验证码”按钮的单击时间,并且自信AJAX请求。代码如下:
复制代码- $(function () {
- $('#captcha-btn').on("click", function (event) {
- event.preventDefault();
- // 获取邮箱
- var email = $("input[name='email']").val();
- zlajax.get({
- url: "/user/mail/captcha?mail=" + email
- }).done(function (result) {
- alert("验证码发送成功!");
- }).fail(function (error) {
- alert(error.message);
- })
- });
- });
复制代码 上述代码:id为captcha-btn的按钮绑定了单击变乱。
单击按钮后,先是获取用户输入的邮箱,然后通过zlajax.get方法发送请求,URL不需要带域名,向以/开头的URL发送请求,欣赏器会自动使员工当前域名。
如果请求乐成,会实行done函数,如果请求失败,会实行fail函数。
由于js文件必须要加载到模版中才能生效,以是,打开templates/front/register.html文件,将head这个block的中代码修改如下:
- {% extends 'front/base.html' %}
- {% block title %}
- 知了课堂注册
- {% endblock %}
- {% block head %}
- <link rel="stylesheet" href="{{ url_for('static',filename='front/css/sign.css') }}">
- <script src="{{ url_for('static',filename='front/js/register.js') }}"></script>
- {% endblock %}
- {% block body %}
- <h1 class="page-title">注册</h1>
- <div class="sign-box">
- <form action="" id="register-form">
- <div class="form-group">
- <div class="input-group">
- <input type="text" class="form-control" name="email" placeholder="邮箱">
- <div class="input-group-append">
- <button id="captcha-btn" class="btn btn-outline-secondary">发送验证码</button>
- </div>
- </div>
- </div>
- <div class="form-group">
- <input type="text" class="form-control" name="captcha" placeholder="邮箱验证码">
- </div>
- <div class="form-group">
- <input type="text" class="form-control" name="username" placeholder="用户名">
- </div>
- <div class="form-group">
- <input type="password" class="form-control" name="password" placeholder="密码">
- </div>
- <div class="form-group">
- <input type="password" class="form-control" name="confirm_password" placeholder="确认密码">
- </div>
- <div class="form-group">
- <a href="#" class="signup-link">返回登录</a>
- <a href="#" class="resetpwd-link" style="float: right">找回密码</a>
- </div>
- </form>
- </div>
- {% endblock %}
复制代码 实现注册功能
首先,在templates/front/register.html中的form标签上添加action和method属性。代码如下:
- {% extends 'front/base.html' %}
- {% block title %}
- 知了课堂注册
- {% endblock %}
- {% block head %}
- <link rel="stylesheet" href="{{ url_for('static',filename='front/css/sign.css') }}">
- <script src="{{ url_for('static',filename='front/js/register.js') }}"></script>
- {% endblock %}
- {% block body %}
- <h1 class="page-title">注册</h1>
- <div class="sign-box">
- <form action="{{ url_for('user.register') }}" method="post" id="register-form">
- <div class="form-group">
- <div class="input-group">
- <input type="text" class="form-control" name="email" placeholder="邮箱">
- <div class="input-group-append">
- <button id="captcha-btn" class="btn btn-outline-secondary">发送验证码</button>
- </div>
- </div>
- </div>
- <div class="form-group">
- <input type="text" class="form-control" name="captcha" placeholder="邮箱验证码">
- </div>
- <div class="form-group">
- <input type="text" class="form-control" name="username" placeholder="用户名">
- </div>
- <div class="form-group">
- <input type="password" class="form-control" name="password" placeholder="密码">
- </div>
- <div class="form-group">
- <input type="password" class="form-control" name="confirm_password" placeholder="确认密码">
- </div>
- <div class="form-group">
- <a href="#" class="signup-link">返回登录</a>
- <a href="#" class="resetpwd-link" style="float: right">找回密码</a>
- </div>
- </form>
- </div>
- {% endblock %}
复制代码 表单通常用POST方法,把表单数据提交到视图函数后,需要对先对表单数据做验证,在根目录下,插件一个名叫:forms的Python Package,然后插件user.py文件,代码如下:
- from wtforms import Form, StringField, ValidationError
- from wtforms.validators import Email, EqualTo, Length
- from exts import cache
- from models.user import UserModel
- class RegisterForm(Form):
- email = StringField(validators=[Email(message="请输入正确格式的邮箱!")])
- captcha = StringField(validators=[Length(min=4, max=4, message="请输入正确格式的验证码!")])
- username = StringField(validators=[Length(min=2, max=20, message="请输入正确格式的用户名!")])
- password = StringField(validators=[Length(min=6, max=20, message="请输入正确长度的密码!")])
- confirm_password = StringField(validators=[EqualTo("password", message="两次密码不一致!")])
- def validate_email(self,field):
- email = field.data
- user = UserModel.query.filter_by(email=email).first()
- if user:
- raise ValidationError(message="邮箱已存在")
- def validate_captcha(self,field):
- captcha = field.data
- email = self.email.data
- cache_captcha = cache.get(email)
- if not cache_captcha or captcha != cache_captcha:
- raise ValidationError(message="验证码错误!")
复制代码 创建了一个RegisterForm表单类,然后定义了email等5个字段,并分别指定了验证器,其中email验证器必须要安装第三方python库email_validator,在pycharm的terminal中,输入: pip install email_validator,进行安装。
思量到以后在视图函数中的表单验证失败后,需要吧错误信息传递到模版中,定一个父类,用于从form.errors中提取全部字符串类型的错误信息。
在根目录下的forms文件下新建一个baseform.py文件。然后输入一下代码:
- from wtforms import Form
- class BaseForm(Form):
- @property
- def message(self):
- message_list = []
- if self.errors:
- for errors in self.errors.values():
- message_list.extend(errors)
- return message_list
复制代码 将RegisterFrom的继承关系修改如霞:
- from wtforms import Form, StringField, ValidationError
- from wtforms.validators import Email, EqualTo, Length
- from exts import cache
- from models.user import UserModel
- from .baseform import BaseForm
- class RegisterForm(BaseForm):
- email = StringField(validators=[Email(message="请输入正确格式的邮箱!")])
- captcha = StringField(validators=[Length(min=4, max=4, message="请输入正确格式的验证码!")])
- username = StringField(validators=[Length(min=2, max=20, message="请输入正确格式的用户名!")])
- password = StringField(validators=[Length(min=6, max=20, message="请输入正确长度的密码!")])
- confirm_password = StringField(validators=[EqualTo("password", message="两次密码不一致!")])
- def validate_email(self,field):
- email = field.data
- user = UserModel.query.filter_by(email=email).first()
- if user:
- raise ValidationError(message="邮箱已存在")
- def validate_captcha(self,field):
- captcha = field.data
- email = self.email.data
- cache_captcha = cache.get(email)
- if not cache_captcha or captcha != cache_captcha:
- raise ValidationError(message="验证码错误!")
复制代码 下面,再把RegisterForm导入blueprints/user.py,美满register视图函数,代码如下:
- from flask import Blueprint, render_template, request, current_app, redirect, url_for, flash
- import random
- # 发送邮箱,引入的2个包
- from flask_mail import Message
- from exts import mail, cache, db
- # 导入工具类
- from utils import restful
- from forms.user import RegisterForm
- from models.user import UserModel
- bp = Blueprint("user", __name__, url_prefix="/user")
- @bp.route("/register/", methods=['GET', 'POST'])
- def register():
- if request.method == 'GET':
- return render_template("front/register.html")
- else:
- form = RegisterForm(request.form)
- if form.validate():
- email = form.email.data
- username = form.username.data
- password = form.password.data
- user = UserModel(email=email, username=username, password=password)
- db.session.add(user)
- db.session.commit()
- return redirect(url_for('user.login'))
- else:
- for message in form.messages:
- flash(message)
- return redirect(url_for("user.register"))
- @bp.route('/login/')
- def login():
- return "login"
- @bp.route('/mail/captcha/')
- def mail_catpcha():
- try:
- email = request.args.get("mail")
- digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
- captcha = "".join(random.sample(digits, 4))
- subject = "[知了Python论坛]注册验证码"
- body = f"[知了Python论坛]您的注册验证码是:{captcha},请勿告诉别人!"
- current_app.celery.send_task("send_mail", (email, subject, body))
- cache.set(email, captcha, timeout=100)
- return restful.ok()
- except Exception as e:
- print(e)
- return restful.server_error()
复制代码 表单验证失败的环境下,由于视图函数已经把错误消息添加到flash中,以是模版中可以通过get_flashed_messages获取全部的错误消息。
在templates/front/register.html中的“立刻注册”按钮桑拿添加以下代码:
- {% extends 'front/base.html' %}
- {% block title %}
- 知了课堂注册
- {% endblock %}
- {% block head %}
- <link rel="stylesheet" href="{{ url_for('static',filename='front/css/sign.css') }}">
- <script src="{{ url_for('static',filename='front/js/register.js') }}"></script>
- {% endblock %}
- {% block body %}
- <h1 class="page-title">注册</h1>
- <div class="sign-box">
- <form action="{{ url_for('user.register') }}" method="post" id="register-form">
- <div class="form-group">
- <div class="input-group">
- <input type="text" class="form-control" name="email" placeholder="邮箱">
- <div class="input-group-append">
- <button id="captcha-btn" class="btn btn-outline-secondary">发送验证码</button>
- </div>
- </div>
- </div>
- <div class="form-group">
- <input type="text" class="form-control" name="captcha" placeholder="邮箱验证码">
- </div>
- <div class="form-group">
- <input type="text" class="form-control" name="username" placeholder="用户名">
- </div>
- <div class="form-group">
- <input type="password" class="form-control" name="password" placeholder="密码">
- </div>
- <div class="form-group">
- <input type="password" class="form-control" name="confirm_password" placeholder="确认密码">
- </div>
- {% with messages=get_flashed_messages() %}
- {% if messahes %}
- <div class="form-group">
- <ul>
- {% for message in messages %}
- <li class="text-danger">{{ message }}</li>
- {% endfor %}
- </ul>
- </div>
- {% endif %}
- {% endwith %}
- <div class="form-group">
- <button class="btn btn-warning btn-block" id="submit-btn">立即注册</button>
- </div>
- <div class="form-group">
- <a href="#" class="signup-link">返回登录</a>
- <a href="#" class="resetpwd-link" style="float: right">找回密码</a>
- </div>
- </form>
- </div>
- {% endblock %}
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |