web应用框架简介及手撸web框架


web应用框架简介及手撸web框架

在前面我我们介绍了软件开发结构流程及演变过程

这里我们简单回顾一下。

软件开发架构分为两种

  • c/s架构:客户端软件(client)—服务端软件(server)
  • b/s架构 :浏览器(Browser)------服务端软件(server)

总结:BS本质上也是CS架构

一、web应用程序是什么?

Web应用程序是一种可以通过Web访问的应用程序,程序的最大好处是用户很容易访问应用程序,用户只需要有浏览器即可,不需要再安装其他软件

应用程序有两种模式C/S、B/S。C/S是客户端/服务器端程序,也就是说这类程序一般独立运行。而B/S就是浏览器端/服务器端应用程序,这类应用程序一般借助IE等浏览器来运行。

WEB应用程序一般是B/S模式。Web应用程序首先是“应用程序”,和用标准的程序语言,如C、C++等编写出来的程序没有什么本质上的不同。然而Web应用程序又有自己独特的地方,就是它是基于Web的,而不是采用传统方法运行的。换句话说,它是典型的浏览器/服务器架构的产物。

1.web应用的优点

  • 网络应用程序不需要任何复杂的“展开”过程,你所需要的只是一个适用的浏览器;
  • 网络应用程序通常耗费很少的用户硬盘空间,或者一点都不耗费;
  • 它们不需要更新,因为所有新的特性都在服务器上执行,从而自动传达到用户端;
  • 网络应用程序和服务器端的网络产品都很容易结合,如email功能和搜索功能;
  • 因为它们在网络浏览器窗口中运行,所以大多数情况下它们是通过跨平台使用的 (例如Windows,Mac,Linux等等)

2.web应用程序的缺点

  • 网络应用程序强调浏览器的适用性。如果浏览器方没有提供特定的功能,或者弃用特定的平台或操作系统版本(导致不适用),就会影响大量用户;
  • 网络应用依靠互联网远程服务器端的应用文件。因此,当连接出问题时,应用将不能正常使用。
  • 许多网络应用程序不是开源的,只能依赖第三方提供的服务,因此不能针对用户定制化、个性化,而且大多数情况下用户不能离线使用,因而损失了很多灵活性;
  • 它们完全依赖应用服务商的可及性。如果公司倒闭,服务器停止使用,用户也无法追索以前的资料。对比而看,即使软件制造商倒闭了,传统的安装软件也可以继续运行,尽管不能再更新或有其他用户服务;
  • 相似地,提供方公司对软件和其功能有了更大的控制权。只要他们愿意就能为软件添加新特性,即使用户想等bugs先被解决再更新。跳过较差的软件版本也不可能了。公司可以强加不受欢迎的特性给用户,也可以随意减少带宽来削减开支。
  • 公司理论上可以检索任何的用户行为。这有可能引起隐私安全问题。

3.B/S架构优点

浏览器/服务器架构(Browser/Server,简称B/S)能够很好地应用在广域网上,成为越来越多的企业的选择。浏览器/服务器架构相对于其他几种应用程序体系结构,有如下3方面的优点:

  • 这种架构采用Internet上标准的通信协议(通常是TCP/IP协议)作为客户机同服务器通信的协议。这样可以使位于Internet任意位置的人都能够正常访问服务器。对于服务器来说,通过相应的Web服务和数据库服务可以对数据进行处理。对外采用标准的通信协议,以便共享数据。
  • 在服务器上对数据进行处理,就处理的结果生成网页,以方便客户端直接下载。
  • 在客户机上对数据的处理被进一步简化,将浏览器作为客户端的应用程序,以实现对数据的显示。不再需要为客户端单独编写和安装其他类型的应用程序。这样,在客户端只需要安装一套内置浏览器的操作系统,直接安装一套浏览器,就可以实现服务器上数据的访问。而浏览器是计算机的标准设备

总结一下,本质上:浏览器是一个socket客户端,服务器是一个socket服务端

二、web框架

1.web框架介绍

Web框架(Web framework)是一种开发框架,用来支持动态网站、网络应用和网络服务的开发。这大多数的web框架提供了一套开发和部署网站的方式,也为web行为提供了一套通用的方法。web框架已经实现了很多功能,开发人员使用框架提供的方法并且完成自己的业务逻辑,就能快速开发web应用了。浏览器和服务器的是基于HTTP协议进行通信的。也可以说web框架就是在以上十几行代码基础张扩展出来的,有很多简单方便使用的方法,大大提高了开发的效率。

2.web框架的本质

所有的Web应用其实就是一个socket服务端, 而用户使用的浏览器就是一个socket客户端程序, 明白了Web框架的本质, 我们就可以实现自己的Web框架了

半成品自定义web框架

import socket

server = socket.socket()  # 默认就是基于网络的TCP协议
server.bind(("127.0.0.1",8080))
server.listen(5)

while 1:
    conn,addr = server.accept()
    data = conn.recv(1024)
    print(data)  # 将请求打印出来
    conn.send(b"HTTP?1.1 200 OK\r\n\nLike a child, always believe in hope, I believe the dream")
    conn.close()

将服务端运行,客户端访问服务端,运行结果如下:

image-20210315214405799

由上一小节可知,HTTP协议规定了让大家发送消息、接收消息的时候有个格式依据以后浏览器发送请求信息也好,服务器回复响应信息也罢,都要按照这个规则来

然后pycharm输出结果如下:

# 请求首行
b'GET / HTTP/1.1\r\n  

# 请求头 (下面都是,一大堆的K:V键值对)
Host: 127.0.0.1:8080\r\n          
Connection: keep-alive\r\n
Cache-Control: max-age=0\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3823.400 QQBrowser/10.7.4307.400\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9\r\n
Cookie: csrftoken=WCzjKvmjOSdJbYKs0uIfPtiFfLl04FENb6p9CjypP7ZObcUpydaQPLZN0qPOVqwj\r\n

# 换行
\r\n'

# 请求体
b''       

可以说web服务的本质都是基于这简单的套接字程序扩展出来的。

三、根据不同的路径返回不同的内容

通过以上,我们是实现了一个简易版的web框架

但是存在以下问题:

在用户访问不同网页时候,如何让我们的Web服务根据用户请求的URL不同而返回不同的内容呢?

解决:

其实很简单,我们可以从请求相关数据里面拿到请求URL的路径,然后拿路径做一个判断…

'''
根据不同的URL返回不同的内容
'''
import socket

server = socket.socket()  # 默认就是TCP协议
server.bind(('127.0.0.1',8080))
server.listen(5)

while True:
    conn, addr = server.accept()  # 三次四次挥手
    data = conn.recv(1024)
    res = data.decode('utf8')
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 请求首行,请求头,空行
    path = res.split(' ')[1]   # 字符串切割获取地址
    if path == '/index':       # 判断地址
        # conn.send(b'index')  # 1.如果判断成功则发送请求体
        with open(r'liko.html','rb') as f:  # 2.或者打开文件一内容作为请求体发送
            data = f.read()
            conn.send(data)
    elif path == '/login':   # 1.如果判断为login
        conn.send(b'login')  # 2.就发送b'login'的请求体
    else:
        conn.send(b'404 error')  # 没匹配到则返回404
    conn.close()

liko.html内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>时间因你而美好</title>
</head>
<body>
    <h1 style="color: #31b0d5">嗨,朋友!你来了,你好啊!</h1>
</body>
</html>

接下来我们来测试一下:

image-20210315222326808

image-20210315222455724

image-20210315222602413

image-20210313184213730

存在的问题:
  • 如果网址路径很多,服务端代码重复(因为if…else…会变得非常多。)
  • 手动处理http数据格式过于繁琐
服务器和应用程序

对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。

服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。

应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。

这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。

这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。

WSGI(Web Server Gateway Interface)就是一种规范,它定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦。

常用的WSGI服务器有uwsgi、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。

四、基于wsgiref模块撸

1.wsgiref 模块的作用

  • swgiref模块帮助我们封装了socket 代码
  • 帮我们处理 http 格式的数据

2.便利之处

  • 请求来的时候帮助你自动拆分http格式数据并封装成非常方便处理的数据格式(类似于字典)
  • 响应走的时候帮你将数据再打包成符合http格式的数据

3.实现代码

from wsgiref.simple_server import make_server


# 以函数形式定义功能,扩展方便
def index_func(request):
    return 'index'


def login_func(request):
    return 'login'


def error(request):
    return '404 Not found'


# 地址与功能的对应关系
urls = [
    ('/index', index_func),
    ('/login', login_func)
]


def run_server(request, response):
    """
    函数名定义什么都无所谓,这里我们使用run_server
    :param request:请求相关的所有数据,一个类似字典的形式,"PATH_INFO"正好就是我们要找的地址
                    wsgiref模块帮我们处理好HTTP格式的数据,封装成了字典让你更加方便的操作
    :param response:响应相关的所有数据
    :return:返回给浏览器的数据,返回格式必须是'return [二进制格式的数据]' 这种样式
    """
    response('200 OK', [])  # 响应首行, 响应头
    current_path = request.get("PATH_INFO")  # 找到路径
    func = None  # 定义一个变量, 存储匹配到的函数名
    for url in urls:
        if current_path == url[0]:
            func = url[1]  # 如果匹配到了则将函数名赋值给func
            break  # 匹配之后立刻结束循环
    if func:  # 然后判断一下func是否被赋值了(也就是是否匹配到了)
        data = func(request)  # 执行函数拿到结果,request可有可无,但放进去以后好扩展
    else:
        data = error(request)
    return [data.encode('utf-8')]


if __name__ == '__main__':
    server = make_server('127.0.0.1', 8080, run_server)  # 一旦被访问将会交给run_server处理
    '''
    会实时监听127.0.0.1:8080地址,只要客户端来了
    都会交给run函数处理(加括号触发run函数的运行)
    flask启动源码
        make_server('127.0.0.1',8080,obj)
        __call__
    '''
    server.serve_forever()  # 启动服务端并一直运行

效果展示如下:

image-20210315230104552

image-20210315230136665

image-20210315230232359

4.问题

  • 网址很多的情况下如何匹配
  • 网址多匹配如何解决
  • 功能复杂代码块如何解决

看起来上面的代码还是要挨个写if判断,怎么办?我们还是有办法!

五、封装处理

随着业务越来越多, 功能越来越多, 将所有代码放在同一个文件会带来很多不必要的麻烦, 于是就需要我们分文件放置相关的代码

1.views.py : 只放功能代码

def index_func(request):
    return 'index'

def login_func(request):
    return 'login'

def error(request):
    return '404 errors'
    
def xxx(request):
    pass

2.urls.py : 存放路径与功能的对应关系

from views import *

urls = [
    ('/index',index_func),
    ('/login',login_func)
]

3.run.py : 只放请求与相应处理代码

from wsgiref.simple_server import make_server
from urls import urls
from views import *

def run_server(request,response):
    """
    :param request:请求相关的所有数据,一个类似字典的形式,"PATH_INFO"正好就是我们要找的地址
    :param response:响应相关的所有数据
    :return:
    """
    response('200 OK',[])  # 响应首行, 响应头
    current_path = request.get("PATH_INFO")  # 找到路径
    func = None  # 定义一个变量, 存储匹配到的函数名
    for url in urls:
        if current_path == url[0]:
            func = url[1]  # 如果匹配到了则将函数名赋值给func
            break  # 匹配之后立刻结束循环
    if func:  # 然后判断一下func是否被赋值了(也就是是否匹配到了)
        data = func(request)  # 执行函数拿到结果,request可有可无,但放进去以后好扩展
    else:
        data = error(request)
    return [data.encode('utf-8')]

if __name__ == '__main__':
    server = make_server('127.0.0.1', 8080, run_server)  # 一旦被访问将会交给run_server处理
    server.serve_forever()  # 启动服务端并一直运行

六、返回静态页面

静态页面:数据都是写死的,固定不变的。

解决了不同URL返回不同内容的问题, 但是我不想仅仅返回几个字符串, 我想给浏览器返回完整的HTML内容, 对此我们只需要通过 open 打开 HTML文件将内容读出来再发送给浏览器就行了

  • 修改 view.py 文件
def index_func(request):
    return 'index'


def login_func(request):
    with open(r"./login.html", "r", encoding="utf-8")as f:
        res = f.read()  # 打开文件读出内容,再返回文件内容
    return res


def error(request):
    return '404 errors'


def xxx(request):
    pass

image-20210313212151474

七、返回动态页面

动态页面 : 数据来源于后端 (代码或者数据库)

1.示例1 : 访问网址展示当前时间

  • 由后端生成时间不能改展示到HTML页面中
'''
view.py 文件
'''
def index_func(request):
    return 'index'

def login_func(request):
    from datetime import datetime
    now_time = datetime.now().strftime("%Y-%m-%d %X")
    with open(r"./login.html", "r", encoding="utf-8")as f:
        res = f.read().replace("datetime1",now_time)
    return res

def error(request):
    return '404 errors'

image-20210313215355990

2.示例二:从数据库中拿到数据

上面我们需要手动的replace更换HTML文件中的代码,接下来,我们用wsgiref再通过jinja2优化上面的replace,再通过pymysql实现后端获取数据库中数据展示到前端页面。

2.1 jinja2模块介绍
jinja2模块的作用:
  • jiaja2的原理就是字符串替换,我们只要在HTML页面中遵循jinja2的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容
jinja2模板语法:
'''定义变量使用双花括号'''
{{ user_list }}

'''for 循环使用双花括号+百分号'''
{{% for user_dict in user_list %}}
{{ user_dict.id}}	# 支持python操作对象的方式取值
{{% endfor %}}
下载安装jinja2模块
pip3 install jinji2

# ps:如果出现网络请求超时或404等情况可以使用镜像源安装
豆瓣源 : http://pypi.douban.com/simple/
清华源: https://pypi.tuna.tsinghua.edu.cn/simple

使用方法 : pip install -i https://pypi.tuna.tsinghua.edu.cn/simple jinja2

ps:该模块是flask框架必备的模块,所以下载flask也会自动下载该模块。

2.2 用wsgiref再通过jinja2优化上面的replace,再通过pymysql实现后端获取数据库中数据展示到前端页面

首先准备一张数据表

mysql> drop database db666;	#删除数据表,重新创建数据表,或直接创建一个没有的数据表,在生产线中需谨慎
Query OK, 0 rows affected (0.58 sec)

mysql> create database db666 charset utf8;
Query OK, 1 row affected (0.00 sec)

mysql> use db666;
Database changed

mysql> create table user(
    ->     id int primary key auto_increment,
    ->     username varchar(25) not null,
    ->     sex enum('man','woman'),
    ->     password varchar(255) not null,
    ->     hobbies set('吃螃蟹','吃生蚝','肯德基','大闸蟹')
    -> );
Query OK, 0 rows affected (0.86 sec)

mysql> insert into user(username,sex,password,hobbies) values
    ->     ('lili','man','li1314420','吃虾米'),
    ->     ('shawn','woman','s1314420','大闸蟹,肯德基'),
    ->     ('hxx','woman','h1314420','大闸蟹,吃生蚝'),
    ->     ('lxx','man','l1314420','大闸蟹,吃虾米'),
    ->     ('zxx','man','z1314420','吃虾米,肯德基'),
    ->     ('mxx','man','m1314420','肯德基,吃生蚝');
Query OK, 6 rows affected, 3 warnings (0.12 sec)
Records: 6  Duplicates: 0  Warnings: 3
               
mysql> select * from user;
+----+----------+-------+-----------+---------------------+
| id | username | sex   | password  | hobbies             |
+----+----------+-------+-----------+---------------------+
|  1 | lili     | man   | li1314420 |                     |
|  2 | shawn    | woman | s1314420  | 肯德基,大闸蟹       |
|  3 | hxx      | woman | h1314420  | 吃生蚝,大闸蟹       |
|  4 | lxx      | man   | l1314420  | 大闸蟹              |
|  5 | zxx      | man   | z1314420  | 肯德基              |
|  6 | mxx      | man   | m1314420  | 吃生蚝,肯德基       |
+----+----------+-------+-----------+---------------------+
6 rows in set (0.00 sec)

image-20210316223839936

  • view.py文件
def index_func(request):
    return 'index'


def login_func(request):
    from datetime import datetime
    now_time = datetime.now().strftime("%Y-%m-%d %X")
    with open(r"./login.html", "r", encoding="utf-8")as f:
        res = f.read().replace("datetime1", now_time)
    return res


# 从数据库获取数据
def get_db_func(request):
    from jinja2 import Template
    import pymysql
    conn = pymysql.connect(host='127.0.0.1',
                           port=3306,
                           user='root',
                           password='123',
                           db='db666',
                           charset='utf8',
                           autocommit=True)
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    sql = 'select * from user'
    rows = cursor.execute(sql)
    user_list = cursor.fetchall()  # [{},{},{}] 格式
    with open(r'get_db.html', 'r', encoding='utf-8')as f:
        data = f.read()  # 字符串
    temp = Template(data)
    # 将user_list传给HTML页面, 在页面中使用data_list调用
    res = temp.render(data_list=user_list)
    return res


def error(request):
    return '404 errors'

  • urls.py文件
from views import *

urls = [
    ('/index', index_func),
    ('/login', login_func),
    ('/get_db',get_db_func)  # 添加一个新功能
]
  • run.py文件
from wsgiref.simple_server import make_server
from urls import urls
from views import *

def run_server(request,response):
    """
    :param request:请求相关的所有数据,一个类似字典的形式,"PATH_INFO"正好就是我们要找的地址
    :param response:响应相关的所有数据
    :return:
    """
    response('200 OK',[])  # 响应首行, 响应头
    current_path = request.get("PATH_INFO")  # 找到路径
    func = None  # 定义一个变量, 存储匹配到的函数名
    for url in urls:
        if current_path == url[0]:
            func = url[1]  # 如果匹配到了则将函数名赋值给func
            break  # 匹配之后立刻结束循环
    if func:  # 然后判断一下func是否被赋值了(也就是是否匹配到了)
        data = func(request)  # 执行函数拿到结果,request可有可无,但放进去以后好扩展
    else:
        data = error(request)
    return [data.encode('utf-8')]

if __name__ == '__main__':
    server = make_server('127.0.0.1', 8080, run_server)  # 一旦被访问将会交给run_server处理
    server.serve_forever()  # 启动服务端并一直运行
  • get_db.html 文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
    <div class="row">
        <h2 class="text-center">用户信息表</h2>
        <table class="table table-hover table-striped table-bordered">
            <thead>
            <tr>
                <th>ID</th>
                <th>username</th>
                <th>sex</th>
                <th>password</th>
                <th>hobbies</th>
            </tr>
            </thead>
            <tbody>
            {% for user_dict in data_list %} {# 从列表中循环取出字典 #}
            <tr>
                <td>{{ user_dict.id }}</td>
                {# 以类似Python中字典的方式取值 #}
                <td>{{ user_dict.username }}</td>
                <td>{{ user_dict.sex }}</td>
                <td>{{ user_dict.password }}</td>
                <td>{{ user_dict.hobbies }}</td>
            </tr>
            {% endfor %}
            </tbody>
        </table>
    </div>
</div>
</body>
</html>

效果展示:

image-20210316224340386

然后向数据库添加一条记录。

mysql> insert user(username,sex,password,hobbies) values('www','man','123456','吃生蚝,大闸蟹');
Query OK, 1 row affected (1.86 sec)

mysql> select * from user;
+----+----------+-------+-----------+---------------------+
| id | username | sex   | password  | hobbies             |
+----+----------+-------+-----------+---------------------+
|  1 | lili     | man   | li1314420 |                     |
|  2 | shawn    | woman | s1314420  | 肯德基,大闸蟹       |
|  3 | hxx      | woman | h1314420  | 吃生蚝,大闸蟹       |
|  4 | lxx      | man   | l1314420  | 大闸蟹              |
|  5 | zxx      | man   | z1314420  | 肯德基              |
|  6 | mxx      | man   | m1314420  | 吃生蚝,肯德基       |
|  7 | www      | man   | 123456    | 吃生蚝,大闸蟹       |
+----+----------+-------+-----------+---------------------+
7 rows in set (0.00 sec)

image-20210316225457386

然后刷新也页面。

image-20210316225629907

八、自定义版本的web框架流程图

d7179dd031e50bf9d32bdc279a80a8e

1. 流程图流程:

浏览器客户端

wsgiref模块
	请求来:处理浏览器请求,解析浏览器HTTP格式的数据,封装成大字典(PATH_INFO中存放的用户访问资源的路径)
    响应去:将数据打包成符合HTTP格式,在返回给浏览器
    
后端:
	urls.py:找出用户输入的路径有么有与视图层的对应关系,如果有则取到views.py找对应的视图函数。
    view.py:
        功能1(静态):视图函数找templates中的html文件,返回给wsgiref做HTTP格式的封装处理,再返回给浏览器.
        功能2(动态):视图函数通过pymysql链接数据库, 通过jinja2模板语法将数据库中取出的数据在tmpelates文件夹下的html文件做一个数据的动态渲染, 最后返回给wsgiref做HTTP格式的封包处理, 再返回给浏览器.
        功能3(动态):也可以通过jinja2模板语法对tmpelates文件夹下的html文件进行数据的动态渲染, 渲染完毕, 再经过wsgiref做HTTP格式的封包处理, 再返回给浏览器.
    templates:html文件

数据库

2. 基本使用流程

# wsgiref模块:  socket服务端(后端)
    from wsgiref.simple_server import make_server

    def run_server(env, response):
        """
        函数名定义什么都无所谓, 我们这里就用run_server.
        :param env:  请求相关的所有数据.
            是一个大字典, wsgiref模块帮你处理好http格式的数据 封装成了字典让你更加方便的操作
        :param response: 响应相关的所有数据.
        :return: 返回给浏览器的数据, 返回个格式必须是'return [二进制格式的数据]' 这种样式
        """
        response('200 OK', [])    # 响应首行 响应头
        return [二进制格式的数据]

    if __name__ == '__main__':
        server = make_server(host, port, app)  # app=run_server
        server.serve_forever()


# urls.py:  路由与视图函数对应关系
    urls = [(路由, 视图函数), ]


# views.py:
    def 视图函数():
        pass

    # pymysql模块: socket服务端(后端)与数据库交互
        import pymysql

        conn = pymysql.connection(host, port, user, password, database, charset='utf8')
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        affected_rows = cursor.execute(sql);
        cursor.fetchall()
        cursor.close()
        conn.close()


    # jinja2模块: 后端与html文件交互.  本质是一种替换操作
        from jinja2 import Template
        temp = Template(data)   # data是要操作的替换的数据
        res = temp.render(user=user_date)   # user是给html页面传的操作变量

# templates文件夹: 管理html文件
    html中使用jinja2模板语法:
    定义变量: {{ user }}
    for循环: {% for xxx in user %} ... {% endfor %}  如果xxx是一个对象, 可以`xxx.id`或者`xxx['id']`取值

3. 自定义web框架步骤总结

  • 手写web框架

  • wsgiref模块

    1. 封装处理了socket代码
    2. 处理了HTTP数据格式
  • 根据不同的功能拆分不同文件

    "urls.py" : 路由与视图函数对应关系
    "views.py" : 视图函数
    "templates" : 模板文件夹(存放HTML文件)
        
    1.第一步添加路由与视图函数的对应关系
    2.去views中书写功能代码
    3.如果需要使用到html则去模板文件夹中操作
    
  • jinja2模板语法

    // 定义变量, 双花括号
    {{ user_list }}
    
    // for 循环, 花括号 + 百分号
    {% for user_dict in user_list %}
    {{ user_dict.id }}  # 支持Python操作对象的方式取值
    {% endfor %}
    
  • 流程图

九、python三大主流web框架

1. 三大主流web框架

Django框架:
  • 特点:大而全,自带的功能组件非常多,类似于航空母舰。
  • 不足:有时候过于笨重
flask框架:
  • 特点:小儿精,自带功能特别特别特别特别少,类似于游骑兵,但是第三方模块非常之多,如果把第三方模块全部叠加起来完全可以盖过django
  • 不足:比较依赖于第三方的开发者,有时候也会受限于第三方模块

PS :三行代码就可以启动一个flask后端服务

tornado框架
  • 特点: 异步非阻塞 速度非常的快 快到可以开发游戏服务器

ps:Sanic、FastAPI…

2. web框架三部分

  • A:socket部分
  • B:路由与视图匹配部分
  • C:模板语法部分

3. 三种主流框架三部分的使用情况

Django

A:用的是别人的(wsgiref模块)

B:用的是自己的

C:用的是自己的

flask

A:用的是别人的(werkzeug(内部还是wsgiref模块))

B:自己写的

C:用的是别人的(jinja2)

tornado

A,B,C都是自己写的

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贾维斯Echo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值