Sanic 学习笔记
2023.11.13 - 2023.11.19
学习使用Sanic,浅显的使用上手很快,并创建了一个项目,担心遗忘,特写此文档以记录学习的内容。
环境说明
所用python版本为3.8.12,虚拟环境中安以下包:
名称 | 版本 | 作用 | 安装 |
---|---|---|---|
python | 3.11.1 | 语言 | |
mysql | 8.0.32 | 数据库 | |
sanic | 23.6.0 | 构建WEB及运行WEB服务 | pip install 包名 |
sanic-ext | 23.6.0 | sanic扩展 | ^ |
sanic-auth | 0.3.0 | 用户验证 | ^ |
Sanic-CookieSession | 0.3.1 | 保持用户会话 | ^ |
sanic-jinja2 | 2022.11.11 | 模板引擎 | ^ |
pymysql | 1.1.0 | mysql数据库连接 | ^ |
cryptography | 41.0.5 | 密码学包,解决使用mysql 8.0新的加密方式登录时遇到的问题 | ^ |
具体实现
1.创建项目结构
│ app.py 主程序文件,用于项目启动
├─project 项目文件夹,用于存放项目所需要使用的文件
│ __init__.py 整合项目文件夹下的所有蓝图
│ core.py 用于存放项目共享对象的文件
│ user_bp.py 用户登陆的蓝图
│ home_bp.py 首页蓝图
├─static 静态文件夹
│ ├─css
│ ├─images
│ └─js
└─templates 用于存放html模板的文件夹
├─index.html 首页模板
├─login.html 用户登陆模板
└─userlist.html 用户列表模板
Note:根据需要可能会有变化,如可能会增一个用于存放用户上传文件的files文件夹等
2.编辑代码,实现项目功能
(1) app_bp.py文件,主程序
# 导入Sanic类
from sanic import Sanic
# 从project中导入蓝图组p_bp(p_bp定义在__init__.py)
from project import p_bp
# 导入SanicJinja2类
from sanic_jinja2 import SanicJinja2
# 导入sanic_cookiesession
import sanic_cookiesession
# 导入共享的auth实例
from project.core import auth
# 创建Sanic实例app
app = Sanic(__name__)
# 配置静态文件目录
app.static("/static", "./static")
# 以Sanic实例app做为参数,创建SanicJinja2实例jinja
jinja = SanicJinja2(app)
# 配置用户认证login参数为路由login
app.config.AUTH_LOGIN_ENDPOINT = "user.login"
# 配置用户会话保持时间为1小时
app.config.KEEP_ALIVE_TIMEOUT = 3600
app.config.SESSION_COOKIE_SECRET_KEY = "study sanic"
# 加载auth用户认证
auth.setup(app)
# 保持会话
sanic_cookiesession.setup(app)
# 注册蓝图到应用
app.blueprint(p_bp)
if __name__ == "__main__":
# 全部IP,80端口,调试开,CPU全开,自动重新加载
app.run(host="0.0.0.0", port=80, debug=True, fast=True, auto_reload=True)
(2) __init__py文件,整合蓝图组
# 导入蓝图类
from sanic import Blueprint
# 导入home_bp中的蓝图实例home
from .home_bp import home
# 导入user_bp中的蓝图实例user
from .user_bp import user
# 定义蓝图组p_bp
p_bp = Blueprint.group(home, user)
(3) core.py文件,共享定义
# 导入pymysql
import pymysql
# 导入Auth实现用户认证
from sanic_auth import Auth
# 这个Auth对象被所有蓝图共享
auth = Auth()
# ------------------- mysql 工具类开始 -------------------
"""
封装一个mysql工具类(需要自己写sql语句)
功能:mysql数据库操作
步骤:
1.连接数据库
2.通过连接对象,获取游标对象
3.增删改查操作
方法:
1.查
2.增删改 commit,rollback
"""
# 定义数据库连接参数定义为字典
config = {
"host": "127.0.0.1",
"port": 3306,
"user": "root",
"password": "123456",
"charset": "utf8",
"database": "sanic_test_db",
}
# 定义封装工具类
class MysqlDb:
# 初始化方法
def __init__(self):
# 初始化方法中调用连接数据库方法
self.conn = self.get_conn()
# 初始化方法中调用获取游标的方法
self.cursor = self.get_cursor()
# 连接数据库的方法
def get_conn(self):
# **config代表不定长参数
conn = pymysql.connect(**config)
return conn
# 利用连接对象获取游标对象
def get_cursor(self):
"""
1.创建游标对象时加入pymysql.cursors.DictCursor参数,使游标对象返回的查询结果集为字典
2.在响应返回json数据里加入ensure_ascii=False参数,否则汉字有可能显示为u+数字的形式,例如
json(result,ensure_ascii=False)
"""
cursor = self.conn.cursor(pymysql.cursors.DictCursor)
return cursor
# 查询sql语句返回所有数据
def select_all(self, sql):
self.cursor.execute(sql)
return self.cursor.fetchall()
# 查询sql语句返回一条数据
def select_one(self, sql):
self.cursor.execute(sql)
return self.cursor.fetchone()
# 查询sql语句返回几条数据
def select_many(self, sql, num):
self.cursor.execute(sql)
return self.cursor.fetchmany(num)
# 增删改除了sql语句不一样,其他都是一样的,都需要提交
def commit_data(self, sql):
try:
# 执行语句
self.cursor.execute(sql)
# 提交
self.conn.commit()
return 1
except Exception as e:
self.conn.rollback()
return 0
# 当对象被销毁时,游标要关闭,连接也要关闭
# 创建时先创建连接后创建游标,关闭时要先关闭游标后关闭连接
def __del__(self):
self.cursor.close()
self.conn.close()
# ------------------- mysql 工具类结束 -------------------
if __name__ == "__main__":
mydb = Mysqldb()
sql = "select HEX(uiduser) as uiduser, username, password from sanic_test_db.user;"
print(mydb.select_all(sql))
(4) home_bp.py文件,首页蓝图
# 导入蓝图类
from sanic import Blueprint
# 导入模板类
from sanic_jinja2 import SanicJinja2
# 导入auth共享实例
from .core import auth
# 创建蓝图对象home
home = Blueprint("home")
@home.route("/home") # 为蓝图注册路由
@SanicJinja2.template("index.html") # 使用html文件模板
@auth.login_required # 用户认证
async def index(request): # 定义首页路由函数
result = auth.current_user(request)
return {"title": result.name} # 返回数据字典,键名title,值为登陆账号username
(5) user_bp.py文件,用户登陆与查询蓝图
# 导入蓝图类和响应方法
from sanic import Blueprint, response
# 导入模板类
from sanic_jinja2 import SanicJinja2
# 导入自定义的数据库工具类
from .core import MysqlDb
# 导入用户认证实例
from .core import auth
# 导入用户认证User类
from sanic_auth import User
# 定义蓝图user
user = Blueprint("user")
# 定义登陆路由login、加载html模板、声明响应函数login
@user.route("/login", methods=["GET", "POST"])
@SanicJinja2.template("login.html")
async def login(request):
# 当请求方法为POST
if request.method == "POST":
# 从请求表单中取得账户
username = request.form.get("username")
# 从请求表单中取得密码
password = request.form.get("password")
# 创建数据库工具实例
mydb = MysqlDb()
# 构造查询语句
sql = f"select HEX(uiduser) as uiduser from sanic_test_db.user where username='{username}' and password='{password}';"
# 调用查询一条数据方法查询数据
result = mydb.select_one(sql)
# 如果有查询结果
if result:
# 声明用户实例user
user = User(id=result["uiduser"], name=username)
# 写入认证对象
auth.login_user(request, user)
# 重定向到home
return response.redirect("/home")
else:
# 没有查询结果返回错误信息
return {"msg": "invalid username or password"}
elif request.method == "GET":
# 当请求方法为GET,返回数据为提示输入账号密码
return {"msg": "please input username & password"}
# 定义用户退出路由logout,声明响应函数logout,并对响应函数进行用户认证
@user.route("/logout")
@auth.login_required
async def logout(request):
auth.logout_user(request)
return response.redirect("/login")
# 定义根路由,如已登陆,重定向到home,如未登陆,因有认证转到login
@user.route("/")
@auth.login_required
async def default(request):
return response.redirect("/home")
@user.route("/user/list")
@auth.login_required
@SanicJinja2.template("userlist.html")
async def user_list(request):
# 创建数据库工具实例
mydb = MysqlDb()
# 构造查询语句
sql = f"select HEX(uiduser) as uiduser, username, password from sanic_test_db.user;"
# 调用查询一条数据方法查询数据
result = mydb.select_all(sql)
print(result)
print(type(result))
# 如果有查询结果
if result:
# 返回查询结果
return {"result": result}
else:
# 没有查询结果返回错误信息
return {"msg": "invalid username or password"}
(6) 数据库、表及添加测试数据
/* 创建名为 sanic_test_db 的mysql数据库,字符集为utf8,排序规则默认 */
CREATE SCHEMA `sanic_test_db` DEFAULT CHARACTER SET utf8 ;
/* 创建user表,用于用户管理 */
CREATE TABLE `sanic_test_db`.`user` (
`uiduser` BINARY(16) NOT NULL,
`username` VARCHAR(10) NOT NULL,
`password` VARCHAR(16) NOT NULL,
PRIMARY KEY (`uiduser`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COMMENT = '用户表';
/* 添加两条用于测试的记录 */
INSERT INTO `sanic_test_db`.`user`(`uiduser`,`username`,`password`)
VALUES
(UUID_TO_BIN(UUID(),TRUE), 'admin', '1234'),
(UUID_TO_BIN(UUID(),TRUE), 'guest', '1234');
/* 查询测试数据 */
select HEX(uiduser), username, password from sanic_test_db.user;
/* 本机查询结果如下 */
/* 11EE83BB92CB43AD922A2CF05DE219C7 admin 1234 */
/* 11EE83BB92CB5C11922A2CF05DE219C7 guest 1234 */
(7) index.html,首页
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 在title标签中调用传递进来的参数title -->
<title>{{title}}</title>
</head>
<body>
<img src="./static/images/sanic_logo.png">
<h1>wellcome to use Sanic!</h1>
<a href="/logout">退出登陆</a>
</body>
</html>
(8) login.html,登陆页面
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="/login" method="post">
账号:<input type="text" name="username" placeholder="请输入用户账号">
密码:<input type="password" name="password" placeholder="请输入用户密码">
<input type="submit" value="登陆">
</form>
<p style="color: red;">{{msg}}</p>
</body>
</html>
(9) userlist.html,用户列表页
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<table border="1px">
{% for i in result%}
<tr>
<td>{{i.uiduser}}</td>
<td>{{i.username}}</td>
<td>{{i.password}}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
Sanic中文网
Sanic-CookieSession
Sanic-Auth
Sanic-Jinja2
cryptography is required for sha256_password or caching_sh 解决办法
python操作mysql