前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python web框架开发 - 模板功能 - 肥仔们的爱好

Python web框架开发 - 模板功能 - 肥仔们的爱好

作者头像
Devops海洋的渔夫
发布2019-06-02 13:43:41
5610
发布2019-06-02 13:43:41
举报
文章被收录于专栏:Devops专栏Devops专栏Devops专栏

原创仅供学习,转载请注明出处

前情回顾

在前面的功能开发中,我已经写道了Python web框架开发 - 路由功能,此时已经基本讲述了web框架如何控制访问过来的http请求路由到相应的处理方法。

模板功能需求

那么下一步就是要考虑如何使用框架,从数据库中查询数据,然后呈现到html页面中。

例如:准备要呈现一个table表格,此时需要从数据库中读取数据来呈现,那么该怎么做呢?

这个功能在框架中定义为模板功能,例如PHP就有smarty这样的模板引擎,当然python的django也有这样的模板引擎。

那么下面先准备一下准备测试的html代码以及mysql数据。

首先准备一个简单的表格HTML内容,用于测试使用

<!DOCTYPE html>
<html>
<meta charset="utf-8">
<body>
    <table border="1px">
        <thead>
            <th>肥仔id</th>      
            <th>肥仔名称</th>      
            <th>肥仔爱好</th>      
            <th>肥仔职业</th>      
            <th>肥仔居住地</th>      
        </thead>
        <tbody>
            <tr>
                <td>1</td>
                <td>胖子老板</td>
                <td>斗地主</td>
                <td>小卖部老板</td>
                <td>西九龙</td>
            </tr>
            <tr>
                <td>2</td>
                <td>肥仔白</td>
                <td>吃槟榔、打dota</td>
                <td>挨踢男</td>
                <td>铜锣湾</td>
            </tr>
        </tbody>
    </table>
</body>
</html>

将html保存到项目当中,页面打开呈现如下:

好了,上面准备了一个超级简单的table页面。

这上面的每一行数据暂时是通过html里面写死的,那么下面就要准备一些mysql数据,用于后面从mysql中读取,然后再在页面中呈现。

准备mysql数据,从Python 使用pymysql模块操作数据库获取

Mysql创建数据库test_db

CREATE DATABASE IF NOT EXISTS test_db CHARACTER SET utf8 COLLATE utf8_general_ci;

Mysql创建表 - 胖子们的爱好表fatboy_hobby

create table fatboy_hobby(
    id int unsigned primary key auto_increment not null,
    name varchar(40) default null COMMENT "肥仔名称",
    hobby varchar(40) default null COMMENT "肥仔爱好",
    professional varchar(40) default null COMMENT "肥仔职业",
    address varchar(40) default null COMMENT "肥仔居住地"
);

Mysql插入数据

insert into fatboy_hobby VALUES(null,"胖子老板","斗地主","小卖铺老板","西九龙");
insert into fatboy_hobby VALUES(null,"肥仔白","打dota、吃槟榔","挨踢攻城狮","铜锣湾");
insert into fatboy_hobby VALUES(null,"肥仔超","吃槟榔、养养猫","挨踢攻城狮","大上海");

好了,创建有了一个表的数据了,那么下面可以使用pymysql来进行增删查改的操作。

另外,在进行数据查询之前,首先将fat_boy.html放入web项目中,先展示看看。

通过web服务端访问fat_boy.html

虽然已经将html文件放入项目中,但是还是不能直接访问的,需要编写访问fat_boy.html的路由方法。

运行测试如下:

好了,页面也有了。下面就是考虑如何实现模板功能。

模板功能介绍

其实模板功能就是根据html页面的表格,在处理方法的时候,对html里面的数据进行处理。

例如:将mysql查询出来的数据,拼接成html内容,重新返回页面,这时候页面呈现的就是myslq查询出来并且拼接好的html内容。

那么有了大概的理解之后,就来看看实现的思路:

  • 首先从mysql中,将该表格中所需要的字段内容查询出来
  • 然后拼接查询的内容为表格中的html,然后返回浏览器

安装pymysql,并且查询数据

安装pymysql

pip3 install pymysql

在fatboy处理方法中,将fatboy表的数据查询出来

首先将数据查询出来看看先,测试如下:

好了,这样已经可以查询出数据了。

改写HTML内容,替换mysql查询出来的数据,返回浏览器

测试一下看看:

将拼接好的content内容替换到返回浏览器的内容中

首先来测试写写html的替换语句:

In [1]: import re

In [2]: html_content = """"""

In [3]: html_content = """<!DOCTYPE html>
   ...: <html>
   ...: <meta charset="utf-8">
   ...: <body>
   ...:     <table border="1px">
   ...:         <thead>
   ...:             <th>肥仔id</th>      
   ...:             <th>肥仔名称</th>      
   ...:             <th>肥仔爱好</th>      
   ...:             <th>肥仔职业</th>      
   ...:             <th>肥仔居住地</th>      
   ...:         </thead>
   ...:         <tbody>
   ...:             <{content}>
   ...:         </tbody>
   ...:     </table>
   ...: </body>
   ...: </html>"""

In [4]: content = """<tr>
   ...:                 <td>1</td>
   ...:                 <td>胖子老板</td>
   ...:                 <td>斗地主</td>
   ...:                 <td>小卖铺老板</td>
   ...:                 <td>西九龙</td>
   ...:             </tr>
   ...:             """

In [5]: ret = re.sub(r"<{content}>",content,html_content)

In [6]: print(ret)
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<body>
    <table border="1px">
        <thead>
            <th>肥仔id</th>
            <th>肥仔名称</th>
            <th>肥仔爱好</th>
            <th>肥仔职业</th>
            <th>肥仔居住地</th>
        </thead>
        <tbody>
            <tr>
                <td>1</td>
                <td>胖子老板</td>
                <td>斗地主</td>
                <td>小卖铺老板</td>
                <td>西九龙</td>
            </tr>

        </tbody>
    </table>
</body>
</html>

In [7]: 

从上面的测试结果来看,使用ret = re.sub(r"<{content}>",content,html_content)就可以匹配替换后,返回正常的html内容了。

同时,要注意,因为这次匹配后是字符串,需要进行utf-8编码后,才能返回浏览器。

例如:ret.encode("utf-8")

好了,下面实现一下代码看看:

测试一下看看:

可以看出,查询出来的结果编码是正常的,但是原来HTML的内容就出现编码错误的情况了。

那么这种情况该怎么处理呢?

这里就涉及一个接受HTML内容使用的bytes类型转化的问题。

编码问题处理

  • 首先文件打开html读取内容的时候,数据类型是bytes型,此时可以认为以及默认使用了encode("utf-8")编码,所以需要进行反编码decode("utf-8")
  • 因为后续还要正则匹配,那么就使用 str() 转为字符串。
  • 最后在返回浏览器的时候,再统一进行utf-8编码返回(encode("utf-8"))。

测试结果如下:

好了,基本上大致功能就是这样的了。肯定有很多其他地方可以优化,但是目前本篇只要介绍大概实现功能就好了。

完整代码如下:

fat_boy.html

<!DOCTYPE html>
<html>
<meta charset="utf-8">
<body>
    <table border="1px">
        <thead>
            <th>肥仔id</th>      
            <th>肥仔名称</th>      
            <th>肥仔爱好</th>      
            <th>肥仔职业</th>      
            <th>肥仔居住地</th>      
        </thead>
        <tbody>
            <{content}>
        </tbody>
    </table>
</body>
</html>

webserver.py

#coding=utf-8
from socket import *
import re
import multiprocessing
import time
import framework
import sys

class WebServer:

   def __init__(self,server_port):

       # 创建套接字
       self.server_socket = socket(AF_INET, SOCK_STREAM)
       # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时 可以立即绑定7788端口
       self.server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
       # 设置服务端提供服务的端口号
       self.server_socket.bind(('', server_port))
       # 使用socket创建的套接字默认的属性是主动的,使用listen将其改为被动,用来监听连接
       self.server_socket.listen(128) #最多可以监听128个连接

   def start_http_service(self):
       # 开启while循环处理访问过来的请求
       while True:
           # 如果有新的客户端来链接服务端,那么就产生一个新的套接字专门为这个客户端服务
           # client_socket用来为这个客户端服务
           # self.server_socket就可以省下来专门等待其他新的客户端连接while True:
           client_socket, clientAddr = self.server_socket.accept()
           # handle_client(client_socket)
           # 设置子进程
           new_process = multiprocessing.Process(target=self.handle_client,args=(client_socket,))
           new_process.start() # 开启子进程
           # 因为子进程已经复制了父进程的套接字等资源,所以父进程调用close不会将他们对应的这个链接关闭的
           client_socket.close()

   def handle_client(self,client_socket):
       """为一个客户端服务"""
       # 接收对方发送的数据
       recv_data = client_socket.recv(1024).decode("utf-8") #  1024表示本次接收的最大字节数
       # 打印从客户端发送过来的数据内容
       #print("client_recv:",recv_data)
       request_header_lines = recv_data.splitlines()
       for line in request_header_lines:
           print(line)

       # 返回浏览器数据
       # 设置内容body
       # 使用正则匹配出文件路径
       print("------>",request_header_lines[0])
       print("file_path---->","./html/" + re.match(r"[^/]+/([^\s]*)",request_header_lines[0]).group(1))
       ret = re.match(r"[^/]+/([^\s]*)",request_header_lines[0])
       if ret:
          file_path = "./html/" + ret.group(1)
          if file_path == "./html/":
             file_path = "./html/index.html"
          print("file_path *******",file_path)

       # 判断file_path是否py文件后缀,如果是则请求动态资源,否则请求静态资源
       if file_path.endswith(".py"):

           # framework.application(client_socket)
           # 支撑WGSI协议的调用方式
           environ = {}
           environ['REQUEST_URI'] = file_path # 设置需要打开的文件路径
           response_body = framework.application(environ, self.start_response)
           # 设置返回的头信息header
           # 1.拼接第一行HTTP/1.1 200 OK + 换行符内容
           response_headers = "HTTP/1.1 " + self.application_header[0] + "\r\n"
           # 2.循环拼接第二行或者多行元组内容:Content-Type:text/html
           for var in self.application_header[1]:
               response_headers += var[0]+":"+var[1] + "\r\n"
           # 3.空一行与body隔开
           response_headers += "\r\n"
           # 4.打印看看header的内容信息
           print("response_header=")
           print(response_headers)

           # 设置返回的浏览器的内容
           client_socket.send(response_headers.encode("utf-8"))
           client_socket.send(response_body)

       else:
           # 请求静态资源
           try:
              # 设置返回的头信息 header
              response_headers = "HTTP/1.1 200 OK\r\n" # 200 表示找到这个资源
              response_headers += "\r\n" # 空一行与body隔开
              # 读取html文件内容
              file_name = file_path # 设置读取的文件路径
              f = open(file_name,"rb") # 以二进制读取文件内容
              response_body = f.read()
              f.close()
              # 返回数据给浏览器
              client_socket.send(response_headers.encode("utf-8"))   #转码utf-8并send数据到浏览器
              client_socket.send(response_body)   #转码utf-8并send数据到浏览器
           except:
              # 如果没有找到文件,那么就打印404 not found
              # 设置返回的头信息 header
              response_headers = "HTTP/1.1 404 not found\r\n" # 200 表示找到这个资源
              response_headers += "\r\n" # 空一行与body隔开
              response_body = "<h1>sorry,file not found</h1>"
              response = response_headers + response_body
              client_socket.send(response.encode("utf-8"))

   def start_response(self,status,header):
       self.application_header = [status,header]
       print("application_header=",self.application_header)

def main():
    # 通过sys.argv来获取服务端的端口号
    # server_port = int(sys.argv[1])
    server_port = 7788

    webserver = WebServer(server_port)
    webserver.start_http_service()

if __name__ == "__main__":
    main()

framework.py

import re
from pymysql import *

# 设置路由对应的字典
route_dict = dict()
"""
route_dict = {
    "index" : index,
    "page_about" : page_about,
}
"""
def route(url):
    def set_route(func):
        # 设置字典映射
        route_dict[url] = func
        def call_func(file_path):
            return func(file_path)
        return call_func
    return set_route

# index页面
@route("index")
def index(file_path):
    file_path = re.sub(r".py", ".html", file_path)
    with open(file_path,"rb") as f:
        return f.read()

# page_about页面
@route("page_about")
def page_about(file_path):
    file_path = re.sub(r".py", ".html", file_path)
    with open(file_path,"rb") as f:
        return f.read()

# fat_boy页面
@route("fat_boy")
def fat_boy(file_path):
    # 创建Connection连接
    conn = connect(host='localhost', port=3306, user='root', password='', database='test_db', charset='utf8')
    # 获得Cursor对象
    cs1 = conn.cursor()
    # 执行select语句,并返回受影响的行数:查询一条数据
    count = cs1.execute('select * from fatboy_hobby;')
    # 打印受影响的行数
    print("查询到%d条数据:" % count)

    # 需要拼接的HTML内容
    """
     <tr>
        <td>1</td>
        <td>胖子老板</td>
        <td>斗地主</td>
        <td>小卖部老板</td>
        <td>西九龙</td>
    </tr>
    <tr>
        <td>2</td>
        <td>肥仔白</td>
        <td>吃槟榔、打dota</td>
        <td>挨踢男</td>
        <td>铜锣湾</td>
    </tr>
    """
    content = ""

    for i in range(count):
        # 获取查询的结果
        result = cs1.fetchone()
        # 打印查询的结果
        print(result)
        # 获取查询的结果
        print("肥仔名称=%s" % result[0])
        print("肥仔爱好=%s" % result[1])
        print("肥仔职业=%s" % result[2])
        print("肥仔居住地=%s" % result[3])
        content += """
             <tr>
                <td>%s</td>
                <td>%s</td>
                <td>%s</td>
                <td>%s</td>
                <td>%s</td>
            </tr>
        """ % (result[0],result[1],result[2],result[3],result[4])
        print("content",content.encode("utf-8"))

    # 关闭Cursor对象
    cs1.close()
    conn.close()

    file_path = re.sub(r".py", ".html", file_path)
    with open(file_path,"rb") as f:
        # html_content = f.read()
        html_content = str(f.read().decode("utf-8"))

    # 使用正则匹配替换<{content}>

    ret = re.sub(r"<{content}>", content, html_content)

    print("html_content=",ret)

    # 返回html内容
    return ret.encode("utf-8")


# 支撑WGSI协议
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=UTF-8')])
    # 接受需要打开的文件路径
    print("动态file_path= ", environ['REQUEST_URI'])

    # 使用正则匹配路径中的函数方法
    try:
        func_name = re.match(r"\./html/([^.]+)",environ['REQUEST_URI']).group(1)
    except Exception as e:
        print("匹配不上,%s" % e)
    else:
        response_body = route_dict[func_name](environ['REQUEST_URI'])

    return response_body
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.01.06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前情回顾
  • 模板功能需求
  • 首先准备一个简单的表格HTML内容,用于测试使用
  • 准备mysql数据,从Python 使用pymysql模块操作数据库获取
  • Mysql创建数据库test_db
  • Mysql创建表 - 胖子们的爱好表fatboy_hobby
  • Mysql插入数据
    • 通过web服务端访问fat_boy.html
      • 模板功能介绍
        • 安装pymysql,并且查询数据
          • 改写HTML内容,替换mysql查询出来的数据,返回浏览器
            • 编码问题处理
              • 完整代码如下:
              相关产品与服务
              云数据库 SQL Server
              腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档