Table of Contents
01有参数的装饰器
02实现自动路由
03装饰器实现路由
04捕捉异常
05路由概念
06静态、动态、伪静态url
07支持伪静态
08 支持mysql
09个人中心页面数据
闭包:拥有一个函数(返回内部函数的引用),与它独有的数据空间(一般函数没有)。并且比全局变量更封闭
返回内部函数引用时不带括号,因为带括号是使用(类创建实例,函数执行),不带括号是指针
装饰器:怎么调用原函数,怎么调用闭包内部函数(参数一致)。通用装饰器一般设置参数为*args,**kwargs,并在调用原函数传值时再次*args,**kwargs拆包,并return原函数返回值
多个装饰器对同一个函数装饰:从下往上装饰,从上往下递归调用执行
需求:不同的函数权限验证的级别不同
#法1 def set_func(func): def call_func(*args,**kwargs): level=args[0] if level==1: print("权限验证级别1") if level==2: print("权限验证级别2") return func() return call_func
@set_func def test1(): print("test") return "ok"
@set_func def test2(): print("test") return "ok" test1(1) test2(1) |
缺点:
带参数装饰器:
def set_level(level_num):#1.接收1,定义函数 def set_func(func):#3.用上一步指针调用装饰 def call_func(*args,**kwargs): if level_num==1: print("权限验证级别1") if level_num==2: print("权限验证级别2") return func() return call_func return set_func#2.返回第二层函数
#调用set_level并将1当作实参传递,获得返回值 #用上一步的返回值set_func当作装饰器对test1装饰(需要多一层函数) @set_level(1) def test1(): print("test") return "ok"
@set_level(2) def test2(): print("test") return "ok"
test1()#4.最后还是调用call_func test2() |
与方案1区别:
加了装饰器之后,调用不变,不用把以前所有的调用都加上参数
调用者无权设置验证级别
已知:web服务器已经将请求通过字典的方式传递到框架的application函数中。该函数决定返回值
当前是检查url,动态资源在函数中一一对应要执行的函数
def application(env, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) file_name=env['PATH_INFO'] if file_name=='/index.py': return index() elif file_name=='/center.py': return center() return ' Hello, web!' |
判断file_name调用函数组装字符串,区分url请求的资源是什么,封装固定代码
需求:真实网站有各种功能,不可能全部手写判断。利用字典思想,选择不同key可以得到不同value,根据不同请求让它自动调用相应函数。可以在value中存函数的引用
全局变量一般使用g_开头或大写命名
#法1 URL_FUNC_DICT={ "/index.py":index, "/center.py":center, } def application(env, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) file_name=env['PATH_INFO'] func=URL_FUNC_DICT[file_name] return func() return ' Hello, web!' |
优点:调用时更简洁
缺点:每次都写字典很麻烦,最好自动生成字典
定义一个空字典,让装饰器往字典中加值
import re URL_FUNC_DICT=dict()
def route(url):#url="/index.py" def set_func(func):#func=index引用 URL_FUNC_DICT[url]=func def call_func(*args,**kwargs): return func(*args,**kwargs) return call_func return set_func
@route("/index.py") def index(): with open("./templates/index.html") as f: content=f.read() my_stock_info="info XXX" content=re.sub(r"\{%content%\}",my_stock_info,content) return content
@route("/center.py") def center(): with open("./templates/center.html") as f: content=f.read() my_stock_info="查询数据库 info XXX" content=re.sub(r"\{%content%\}",my_stock_info,content) return content
def application(env, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) file_name=env['PATH_INFO'] func=URL_FUNC_DICT[file_name] return func() return ' Hello, web!' |
首先将判断改为字典取引用,然后使用字典,装饰器在模块被导入时就已经执行、填充好字典了
请求的url一般会对应某个函数,通过字典or元组可实现
使用带参装饰器组装字典:k(url字符串)-v(函数引用)
通过闭包的数据空间建立字符串到引用的映射
使用了装饰器,index引用指向它对应的call_func,call_func中调用原函数,字典中也保存着原函数的引用。所以装饰只影响手动调用函数,不影响路由
此时如果url为不存在的py会在字典查找时出现异常,也可以先用in判断是否在字典中
但用try捕获异常更好,因为异常可以传递,如果在函数执行时有异常未处理,仍会导致服务器出现问题
def application(env, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) file_name=env['PATH_INFO'] try: return URL_FUNC_DICT[file_name]() except Exception as ret: return "产生了异常:%s" % str(ret)#保证返回web服务器有body return ' Hello, web!' |
如果在捕捉到异常:
'gbk' codec can't decode byte 0xaa in position 225: illegal multibyte sequence |
此种错误,可能是要处理的字符串本身不是gbk编码,但是却以gbk编码去解码 。比如,字符串本身是utf-8的,但是却用gbk去解码utf-8的字符串,所以结果不用说,则必然出错。可以按照如下的步骤进行尝试:
(1)在打开文本时候,可以指明打开方式:
file = open(path, encoding='gbk')或file = open(path, encoding='utf-8')
(2)如果上一步还不能解决,可能是文本中出现的一些特殊符号超出了gbk的编码范围,可以选择编码范围更广的‘gb18030’,如:
file = open(path, encoding='gb18030')
(3)如果上一步还不能解决,说明文中出现了连‘gb18030’也无法编码的字符,可以使用‘ignore’属性忽略非法字符,如:file = open(path, encoding='gb18030', errors='ignore')或者file=open(path).read().decode(‘gb18030’,’ignore’)
路由器:数据转发,IP指明道路。
有两个以上的网卡,一个用于接收数据放到内存,一个将内存中数据发走
来一个请求,调用对应的函数为它服务,使用映射实现
url资源定位符中,真静态url有真实的物理路径:域名/news/1.html
优点:不用经过框架比较快,对关键字SEO搜索引擎优化较好-权重高 排名靠前
缺点:不便于优化修改
动态url(常根据参数给不同参数): 域名/news/1.asp?id=5,常见后缀有jsp、asp、php、py
伪静态url:域名/course/1.html形式,但也是逻辑地址,没有物理地址
其中course作为变量名、1作为值
需求:目前网页是.py结尾,实现伪静态支持
浏览器请求->web server->后缀.py的->frame
↓
直接获取
伪静态的支持应该在服务器修改,如果服务器只给frame转发.py的请求,那么框架没有机会处理伪静态
把html后缀结尾也作为动态处理,静态剩下css/js/png等
同时装饰器中的路由、页面中py结尾的href也改成html
创建数据库stock_db,导入表(如果在Linux环境,则在当前文件夹能看见sql文件的地方source命令运行sql文件)需要的sql文件存放在链接的压缩包中
股票信息页面显示info表数据,个人中心页面显示自己关注的股票信息
在框架中修改,原本content的空缺直接用固定字符串填充,现从数据库查出
Python操作数据库时默认开启事务(特性:原子性、一致性、持久性、隔离性),其中查询不需要commit提交,增删改需要提交
操作流程为:导入、链接数据库、获取游标,execute执行sql语句
因为默认环境要安装该模块,我使用了虚拟环境
from pymysql import connect |
链接时记得改database等值
#创建链接 conn = pymysql.connect(host='localhost',port=3306,user='root',password='123456',database='stock_db',charset='utf8') #获得cursor对象 cursor = conn.cursor() #执行sql语句 sql = """select * from info;""" cursor.execute(sql) # 存储查询出来的数据 data_from_mysql = cursor.fetchall() #每一行是一个元组,多行则外部再套一个元组 #全部关闭 cursor.close() conn.close() #不能直接拿元组替换,需要转成str content=re.sub(r"\{%content%\}",str(strdata_from_mysql),content) |
此时页面展示的数据还是一坨,拿一个前端模板套tr、td
向模板填充数据库中一一对应的各值,此处要注意确认使用的模板html中确实有{%content%}
# 3. 将mysql查询出来的数据替换到模板中 line_html = """
| ||||||||
%s | %s | %s | %s | %s | %s | %s | %s |
|
最后添加还需要一个值,不过先留着
页面中能点的能看的都由前端负责,后端只需要提供它的数据服务
修改模板
关联两表查询,修改sql
根据focus表中的info_id在info表查找
select * from info as i inner join focus as f on i.id=f.info_id; |
查找结果: +----+--------+----------+---------+----------+-------+-------+------------+----+------------------+---------+ | id | code | short | chg | turnover | price | highs | time | id | note_info | info_id | +----+--------+----------+---------+----------+-------+-------+------------+----+------------------+---------+ | 36 | 300268 | 万福生科 | -10.00% | 0.27% | 31.77 | 13.57 | 2017-04-10 | 2 | 你确定要买这个? | 36 | | 37 | 300280 | 南通锻压 | 3.31% | 0.66% | 32.2 | 32 | 2017-04-11 | 3 | 利好 | 37 | | 88 | 601678 | 滨化股份 | 0.13% | 2.47% | 7.92 | 7.91 | 2017-07-20 | 9 | | 88 | | 89 | 601918 | 新集能源 | 1.23% | 3.11% | 4.93 | 4.92 | 2017-07-19 | 10 | | 89 | | 1 | 000007 | 全新好 | 10.01% | 4.40% | 16.05 | 14.6 | 2017-07-18 | 13 | | 1 | +----+--------+----------+---------+----------+-------+-------+------------+----+------------------+---------+ |
Info中不需要的数据有序号id、时间,focus中需要备注信息
select i.code,i.short,i.chg,i.turnover,i.price,i.highs,f.note_info from info as i inner join focus as f on i.id=f.info_id; |
Center函数
@route("/center.py") def center(): with open("./templates/center.html", encoding='UTF-8') as f: content = f.read() conn = pymysql.connect(host='localhost',port=3306,user='root',password='123456',database='stock_db',charset='utf8') cursor = conn.cursor() #修改sql语句 sql = """select i.code,i.short,i.chg,i.turnover,i.price,i.highs,f.note_info from info as i inner join focus as f on i.id=f.info_id; """ cursor.execute(sql) data_from_mysql = cursor.fetchall() cursor.close() conn.close() #修改模板 line_html = """
| ||||||||
%s | %s | %s | %s | %s | %s | %s |
|
|
新增请求流程:确定需要哪些url,编写对它处理的函数,使用装饰器
而web框架核心基本功能不变:application、route装饰器及其字典
面向切面(新功能只需要定义一个函数让它知道,就像往其中插一个切面,只需要注意这一个面的功能,不需要关注整体)用装饰器加一个功能,用原本的框架调用