python笔记4:编写web框架支持路由与mysql

Table of Contents

01有参数的装饰器

02实现自动路由

03装饰器实现路由

04捕捉异常

05路由概念

06静态、动态、伪静态url

07支持伪静态

08 支持mysql

09个人中心页面数据


01有参数的装饰器

闭包:拥有一个函数(返回内部函数的引用),与它独有的数据空间(一般函数没有)。并且比全局变量更封闭

扫描二维码关注公众号,回复: 13139299 查看本文章

返回内部函数引用时不带括号,因为带括号是使用(类创建实例,函数执行),不带括号是指针

装饰器:怎么调用原函数,怎么调用闭包内部函数(参数一致)。通用装饰器一般设置参数为*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)

缺点:

  1. 装饰器一般不能影响原函数调用,定义函数时不用参数,调用时就不应该需要参数。而这个参数是给装饰器传的,不是给原函数传的。如果函数多次调用,则每次都需要给函数传对应装饰器需要的参数。
  2. 装饰器需要验证的级别应该是在装饰函数时规定好的,如果在调用函数时自己传该参数,可自定义验证级别,不能有效验证

带参数装饰器:

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区别:

加了装饰器之后,调用不变,不用把以前所有的调用都加上参数

调用者无权设置验证级别

02实现自动路由

已知: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 '<h1>Hello, web!</h1>'

判断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 '<h1>Hello, web!</h1>'

优点:调用时更简洁

缺点:每次都写字典很麻烦,最好自动生成字典

03装饰器实现路由

定义一个空字典,让装饰器往字典中加值

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 '<h1>Hello, web!</h1>'

首先将判断改为字典取引用,然后使用字典,装饰器在模块被导入时就已经执行、填充好字典了

04捕捉异常

请求的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 '<h1>Hello, web!</h1>'

如果在捕捉到异常:

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

05路由概念

路由器:数据转发,IP指明道路。

有两个以上的网卡,一个用于接收数据放到内存,一个将内存中数据发走

来一个请求,调用对应的函数为它服务,使用映射实现

06静态、动态、伪静态url

url资源定位符中,真静态url有真实的物理路径:域名/news/1.html

优点:不用经过框架比较快,对关键字SEO搜索引擎优化较好-权重高 排名靠前

缺点:不便于优化修改

动态url(常根据参数给不同参数): 域名/news/1.asp?id=5,常见后缀有jsp、asp、php、py

伪静态url:域名/course/1.html形式,但也是逻辑地址,没有物理地址

其中course作为变量名、1作为值

需求:目前网页是.py结尾,实现伪静态支持

07支持伪静态

浏览器请求->web server->后缀.py的->frame

                     ↓

                     直接获取

伪静态的支持应该在服务器修改,如果服务器只给frame转发.py的请求,那么框架没有机会处理伪静态

把html后缀结尾也作为动态处理,静态剩下css/js/png等

同时装饰器中的路由、页面中py结尾的href也改成html

08 支持mysql

创建数据库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 = """

                <tr>

                    <td>%s</td>

                    <td>%s</td>

                    <td>%s</td>

                    <td>%s</td>

                    <td>%s</td>

                    <td>%s</td>

                    <td>%s</td>

                    <td>%s</td>

                    <td>

                        <input type="button" value="添加" id="toAdd" name="toAdd" systemidvaule="000007">

                    </td>

                </tr>

    """

    #创建一个空字符串

    code_html = ""

    #遍历取出的数据,每次放一行模板

    for line_info in data_from_mysql:

        #向模板填充

        code_html+=line_html % (line_info[0],line_info[1],line_info[2],line_info[3],line_info[4],line_info[5],line_info[6],line_info[7])

 

    content=re.sub(r"\{%content%\}",code_html,content)

最后添加还需要一个值,不过先留着

09个人中心页面数据

页面中能点的能看的都由前端负责,后端只需要提供它的数据服务

修改模板

关联两表查询,修改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 = """

                <tr>

                    <td>%s</td>

                    <td>%s</td>

                    <td>%s</td>

                    <td>%s</td>

                    <td>%s</td>

                    <td>%s</td>

                    <td>%s</td>

                    <td>

                        <a type="button" class="btn btn-default btn-xs" href=""><span class="glyphicon glyphicon-star" aria-hidden="true">→</span>修改</a>

                    </td>

                    <td>

                        <input type="button" value="删除" id="toDel" name="toDel" systemidvaule="000007">

                    </td>

                </tr>

    """

    code_html = ""

    for line_info in data_from_mysql:

        #修改向模板填充的数据个数

        code_html+=line_html % (line_info[0],line_info[1],line_info[2],line_info[3],line_info[4],line_info[5],line_info[6])

 

    content=re.sub(r"\{%content%\}",code_html,content)   

    # html_content = re.sub(r"\{%content%\}", data_from_mysql, html_content)

    return content

新增请求流程:确定需要哪些url,编写对它处理的函数,使用装饰器

而web框架核心基本功能不变:application、route装饰器及其字典

面向切面(新功能只需要定义一个函数让它知道,就像往其中插一个切面,只需要注意这一个面的功能,不需要关注整体)用装饰器加一个功能,用原本的框架调用

猜你喜欢

转载自blog.csdn.net/lagoon_lala/article/details/106173659