Django-手撸简易web框架-实现动态网页-wsgiref初识-jinja2初识-python主流web框架对比-00...

自己动手实现一个简易版本的web框架

在了解python的三大web框架之前,我们先自己动手实现一个。

备注:

这部分重在掌握实现思路,代码不是重点

代码中也有许多细节并未考虑,重在实现思路

手撸一个web服务端

我们一般是使用浏览器当做客户端,然后基于HTTP协议自己写服务端代码作为服务端

先自行去回顾一下HTTP协议这一块儿的知识

import socket
server = socket.socket()  # 基于socket通信(TCP)
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
    conn, addr = server.accept()
    data = conn.recv(2048)  # 接收请求
    print(str(data, encoding='utf-8'))
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 依据HTTP协议,发送响应给客户端(浏览器),这里是响应首行 + 响应头 + 空行
    # response = bytes('<h3>这是响应内容</h3>', encoding='GBK')
    response = '<h3>这是响应内容</h3>'.encode('GBK')  # 我电脑上是GBK编码,所以使用GBK编码将字符串转成二进制
    conn.send(response)  #  继续发送响应体
    conn.close()  # 断开连接(无状态、无连接)

# 浏览器发过来的数据如下
'''
GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: _qddaz=QD.w3c3g1.j2bfa7.jvp70drt; csrftoken=kJHVGICQOglLxJNiui0o0UyxNtR3cXbJPXqaUFs5FoxeezuskRO7jlQE0JNwYXJs


GET /favicon.ico HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Referer: http://127.0.0.1:8080/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: _qddaz=QD.w3c3g1.j2bfa7.jvp70drt; csrftoken=kJHVGICQOglLxJNiui0o0UyxNtR3cXbJPXqaUFs5FoxeezuskRO7jlQE0JNwYXJs
'''

然后右键运行,在浏览器访问 127.0.0.1:8080 即可看到响应数据

关于启动服务器与页面请求(在我处理的时候,页面网络请求会经常处于 pending状态,不是很清楚原因,一般这个情况下,直接重启一下服务器即可)

1725797-20190915012206786-1284670708.png

根据请求 url 做不同的响应处理

上面的代码已经实现了基本请求响应,那如何根据不同的请求作出不同的响应呢?

我们输入不同的url,看看服务器端会返回什么

分析请求

浏览器访问 http://127.0.0.1:8080/index
GET /index HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: _qddaz=QD.w3c3g1.j2bfa7.jvp70drt; csrftoken=kJHVGICQOglLxJNiui0o0UyxNtR3cXbJPXqaUFs5FoxeezuskRO7jlQE0JNwYXJs
    

浏览器访问 http://127.0.0.1:8080/home   
GET /home HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: _qddaz=QD.w3c3g1.j2bfa7.jvp70drt; csrftoken=kJHVGICQOglLxJNiui0o0UyxNtR3cXbJPXqaUFs5FoxeezuskRO7jlQE0JNwYXJs

原来请求首行的 GET 后面跟的就是请求我们想要信息(/index 首页、/home 家)

这些信息也是我们接收到的(data = conn.recv(2048) print(str(data, encoding='utf-8'))),那可不可以取出来,根据值的不同作不同处理呢?

处理请求,获取 url

data = '''GET /home HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: _qddaz=QD.w3c3g1.j2bfa7.jvp70drt; csrftoken=kJHVGICQOglLxJNiui0o0UyxNtR3cXbJPXqaUFs5FoxeezuskRO7jlQE0JNwYXJs'''
print(data.split('\n')[0].split(' ')[1])  # ... ---> GET /home HTTP/1.1 --> ['GET', '/home', 'HTTP/1.1']  --> /home
# /home

依据上述切割规则,我们来对不同的请求作出不同的响应

import socket
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
    conn, addr = server.accept()
    data = conn.recv(2048).decode('utf-8')
    data = data.split('\n')[0].split(' ')[1]
    print(data)
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
    if data == '/index':
        response = '<h3>这里是 index...</h3>'.encode('GBK')
    elif data == '/home':
        response = '<h3>这里是 home...</h3>'.encode('GBK')
    else:
        response = '<h3>404 NOT FOUND...\n找不到您要找的资源...</h3>'.encode('GBK')
    conn.send(response)
    conn.close()

# --- 浏览器请求 http://127.0.0.1:8080/index 的打印信息
# /index
# /favicon.ico
# --- 浏览器请求 http://127.0.0.1:8080/home 的打印信息
# /home
# /favicon.ico
# --- 浏览器请求 http://127.0.0.1:8080/de2332f 的打印信息
# /de2332f
# /favicon.ico

页面成功显示不同的信息

http://127.0.0.1:8080/index

1725797-20190915012146440-1854820402.png

http://127.0.0.1:8080/home

1725797-20190915012136125-688240817.png

http://127.0.0.1:8080/de2332f

1725797-20190915012117075-1589172819.png

404页面也应该算作设计网站的一部分,可以给人不一样的感觉

基于wsgiref模块实现服务端

前面处理 scoket 和 http 的那堆代码通常是不变的,且与业务逻辑没什么关系,如果每个项目都要写一遍,那岂不是很麻烦?那封装成模块嘛~

不过这个操作已经有人帮我们做了,并且封装的更加强大,就是 wsgiref 模块

用wsgiref 模块的做的两件事

  1. 在请求来的时候,自动解析 HTTP 数据,并打包成一个字典,便于对请求发过来的数据进行操作
  2. 发响应之前,自动帮忙把数据打包成符合 HTTP 协议的格式(响应数据格式,不需要再手动写 conn.send(b'HTTP/1.1 200 OK\r\n\r\n') 了),返回给服务端
from wsgiref.simple_server import make_server  # 导模块


def run(env, response):
    """
    先不管这里的 env 和 response 什么个情况

    env:是请求相关的数据,wsgiref帮我们把请求包装成了一个大字典,方便取值
    response:是响应相关的数据
    """
    response('200 OK', [])
    print(env)
    current_path = env.get('PATH_INFO')
    print(current_path)

    if current_path == '/index':
        return ['hello, there is index...'.encode('utf-8')]
    elif current_path == '/login':
        return ['hello, there is login...'.encode('utf-8')]
    else:
        return ['sorry... that pages you want is not found...'.encode('utf-8')]


if __name__ == '__main__':
    # 实时监测 127.0.0.1:8080 地址,一旦有客户端连接,会自动加括号调用 run 方法
    server = make_server('127.0.0.1', 8080, run)
    server.serve_forever()  # 启动服务器


# /index
# ---> env 的数据(手动删减了一些),可以看到其中有个 PATH_INFO 是我们要的东西(还有浏览器版本啊,USER-AGENT啊,客户端系统环境变量啊之类的信息)
'''{'ALLUSERSPROFILE': 'C:\\ProgramData', ...省略部分... , 'COMSPEC': 'C:\\Windows\\system32\\cmd.exe', 'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 60 Stepping 3, GenuineIntel', 'PROCESSOR_LEVEL': '6', 'PROCESSOR_REVISION': '3c03', 'PYTHONIOENCODING': 'UTF-8',  'SESSIONNAME': 'Console', 'SYSTEMDRIVE': 'C:', 'SERVER_PORT': '8080', 'REMOTE_HOST': '', 'CONTENT_LENGTH': '', 'SCRIPT_NAME': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_SOFTWARE': 'WSGIServer/0.2', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/index', 'QUERY_STRING': '', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'text/plain', 'HTTP_HOST': '127.0.0.1:8080', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3', csrftoken=kJHVGICQOglLxJNiui0o0UyxNtR3cXbJPXqaUFs5FoxeezuskRO7jlQE0JNwYXJs', mode='w' encoding='UTF-8'>, 'wsgi.version': (1, 0), }'''

伏笔

1725797-20190915012050943-2116713371.png

拆分服务端代码

服务端代码、路由配置、视图函数,照目前的写法全都冗在一块儿,后期功能扩展时,这个文件会变得很长,不方便维护,所以选择把他拆分开来

就是将服务端代码拆分成如下三部分:

  • server.py 放服务端代码

  • urls.py 放路由与视图函数对应关系

  • views.py 放视图函数/类(处理业务逻辑)

views.py

def index(env):
    return 'index'


def login(env):
    return 'login'

urls.py

from views import *

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

server.py

from wsgiref.simple_server import make_server  # 导模块
from urls import urls  # 引入 urls.py 里的 urls列表(命名的不是很规范)


def run(env, response):
    response('200 OK', [])
    current_path = env.get('PATH_INFO')

    func = None
    for url in urls:
        if current_path == url[0]:
            func = url[1]
            break

    if func:
        res = func(env)
    else:
        res = '404 Not Found.'
    return [res.encode('utf-8')]  # 注意这里返回的是一个列表(可迭代对象才行),wsgiref 模块规定的,可能还有其他的用途吧


if __name__ == '__main__':
    server = make_server('127.0.0.1', 8080, run)
    server.serve_forever()

支持新的请求地址(添加新页面/新功能)

经过上面的拆分后,后续想要支持其他 url,只需要在 urls.py 中添加一条对应关系,在 views.py 中把该函数实现,重启服务器即可访问

以支持 http://127.0.0.1:8080/new_url 访问为例

urls.py

from views import *

urls = [
    ('/index', index),
    ('/login', login),
    ('/new_url', new_url),
]

views.py

def index(env):
    return 'index'


def login(env):
    return 'login'


def new_url(env):
    # 这里可以写一堆逻辑代码
    return 'new_url'

重启服务器,打开浏览器即可访问 http://127.0.0.1:8080/new_url

1725797-20190915012027987-303037309.png

扩展性高了很多,且逻辑更清晰了,更不容易弄错(框架的好处提现,也是为什么脱离了框架不会写的原因,这块代码写的太少,不常用到,没了框架又写不出来)

动态静态网页--拆分模板文件

前面写了那么多,都只是一直在返回纯文本信息,而我们一般请求页面返回的都是浏览器渲染好的华丽的页面,那要怎么放回华丽的页面呢?

页面嘛,就是 HTML + CSS + JS 渲染出来的,所以我们也可以把 HTML文件当成数据放在响应体里直接返回回去

新建一个功能的步骤还是复习一下

  • 在 urls.py 里面加一条路由与视图函数的对应关系
  • 在 views.py 里面加上那个视图函数,并写好内部逻辑代码
  • 重启服务器,浏览器打开页面访问

返回静态页面--案例

这里咱们就接着上面的 new_url 写,用他来返回 一个网页

新建一个 templates 文件夹,专门用来放 HTML 文件

templates/new_url.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>New URL</h1>
    <h5>Wellcome!</h5>
</body>
</html>

views.py

def index(env):
    return 'index'


def login(env):
    return 'login'


def new_url(env):
    # 读取并把 new_url 文件返回给客户端(浏览器)
    with open(r'templates/new_url.html', 'rb') as f:
        html_data = f.read()
    return html_data.decode('utf-8')  # 因为 run 函数那里做了 encode, 而二进制数据没有 encode这个方法,所以这里先解码一下,然后那边再编码一下

重启服务器,使用浏览器访问

1725797-20190915012013685-1716272558.png

上面提到了静态页面,那什么是静态页面?什么又是动态页面呢?

  • 静态网页:纯html网页,数据是写死的,所有同url的请求拿到的数据都是一样的

  • 动态网页:后端数据拼接,数据不是写死的,是动态拼接的,比如:

    ​ 后端实时获取当前时间“传递”(塞)给前端页面展示

    ​ 后端从数据库获取数据“传递”给前端页面展示

实现返回时间--插值思路(动态页面)

要怎么在 html 里插入时间呢?

往 html 里的插入?那替换好像也可以达到效果啊?

html_data = f.read() ? 好像 html 被读出出来了,而且还是二进制的,二进制可以 decode 变成字符串,字符串有 replace方法可以替换字符串,那我随便在网页里写点内容,然后替换成时间?

先把基础歩鄹做好

templates/get_time.html 编写展示页面

put_times_here 用来做占位符,一会儿给他替换成时间

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>北京时间:</h1>
    <h1>put_time_here</h1>
</body>
</html>

urls.py 路由与视图函数对应关系

from views import *

urls = [
    ('/index', index),
    ('/login', login),
    ('/new_url', new_url),
    ('/get_time', get_time),
]

views.py 实现视图函数

def index(env):
    return 'index'


def login(env):
    return 'login'


def new_url(env):
    # 读取并把 new_url 文件返回给客户端(浏览器)
    with open(r'templates/new_url.html', 'rb') as f:
        html_data = f.read()
    return html_data


def get_time(env):
    # 读取并把 get_time 文件返回给客户端(浏览器)
    with open(r'templates/get_time.html', 'rb') as f:
        html_data = f.read().decode('utf-8')
    import time
    html_data = html_data.replace('put_time_here', time.strftime("%Y-%m-%d %X"))
    return html_data

重启服务器并打开浏览器访问 http://127.0.0.1:8080/get_time

1725797-20190915011959639-861705325.png

关键思路:相当于占位符,字符串替换,后期把前端要替换的字符的格式统一规定下,方便阅读与统一处理,这其实也就是目前的模版语法的雏形

我们只需要把处理好的字符串(HTML格式的)返回给浏览器,待浏览器渲染即可有页面效果

利用 jinja2 模块实现动态页面

jinja2模块有着一套 模板语法,可以帮我更方便地在 html 写代码(就想写后台代码一样),让前端也能够使用后端的一些语法操作后端传入的数据

安装 jinja2

jinja2 并不是 python 解释器自带的,所以需要我们自己安装

​ 由于 flask 框架是依赖于 jinja2 的,所下载 flask 框架也会自带把 jinja2 模块装上

命令行执行,pip3 install jinja2图形化操作安装(参考 Django 的安装方法)

初步使用

这里只是知道有模板语法这么一个东西可以让我们很方便的往 html 写一些变量一样的东西,并不会讲 jinja2 的语法,后续会有的

案例--展示字典信息

urls.py

from views import *

urls = [
    ('/index', index),
    ('/login', login),
    ('/new_url', new_url),
    ('/get_time', get_time),
    ('/show_dic', show_dic),
]

views.py

def index(env):
    return 'index'


def login(env):
    return 'login'


def new_url(env):
    # 读取并把 new_url 文件返回给客户端(浏览器)
    with open(r'templates/new_url.html', 'rb') as f:
        html_data = f.read()
    return html_data


def get_time(env):
    # 读取并把 get_time 文件返回给客户端(浏览器)
    with open(r'templates/get_time.html', 'rb') as f:
        html_data = f.read().decode('utf-8')
    import time
    html_data = html_data.replace('put_time_here', time.strftime("%Y-%m-%d %X"))
    return html_data


def show_dic(env):
    user = {
        "username": "jason",
        "age": 18,
    }
    with open(r'templates/show_dic.html', 'rb') as f:
        html_data = f.read()

    # 使用 jinja2 的模板语法来将数据渲染到页面上(替换占位符)
    from jinja2 import Template
    tmp = Template(html_data)
    res = tmp.render(dic=user)  # 将字典 user 传递给前端页面,前端页面通过变量名 dic 就能够获取到该字典
    return res

templates/show_dic.html 写页面

jinja2 给字典扩展了点语法支持({{ dic.username }}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>Nice to meet you~ i'm {{ dic.username }} , and i'm {{ dic.age }} years old.</h1>
    <p>username: {{ dic['username']}}</p>
    <p>age: {{ dic.get('age')}}</p>
</body>
</html>

重启服务器并打开浏览器访问 http://127.0.0.1:8080/show_dic

1725797-20190915011940565-583503737.png

为什么说动态?

如果你改变了字典里的值,那么请求这个页面,显示的数据也会跟着改变(注意这个字典一般都是其他地方获取过来的)


模板语法(贴近python语法): 前端也能够使用后端的一些语法操作后端传入的数据
    {{data.password}}  # jinja2 多给字典做了 点语法支持
    ... 其他的语法,写法
    
    for 循环
    {%for user_dict in user_list%}
        <tr>
            <td>{{user_dict.id}}</td>
            <td>{{user_dict.name}}</td>
            <td>{{user_dict.password}}</td>
        </tr>
    {%endfor%}

进阶案例--渲染数据库数据到页面

思路

pymsql 从数据库取数据(指定成 列表套字典 的格式(DictCursor))
后台 python 代码处理数据
交由 jinja2 模块语法渲染到 html 页面上

数据条数不定怎么办?
    有多少条记录就显示多少条呗...循环?
    表格格式先写好,然后循环渲染数据到标签上(特定语法表示循环)
数据准备

创建数据库 django_test_db,然后执行如下 SQL 命令

/*
 Navicat MySQL Data Transfer

 Source Server         : localhost-E
 Source Server Type    : MySQL
 Source Server Version : 50645
 Source Host           : localhost:3306
 Source Schema         : django_test_db

 Target Server Type    : MySQL
 Target Server Version : 50645
 File Encoding         : 65001

 Date: 15/09/2019 00:41:09
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user_info
-- ----------------------------
DROP TABLE IF EXISTS `user_info`;
CREATE TABLE `user_info`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of user_info
-- ----------------------------
INSERT INTO `user_info` VALUES (1, 'jason', '123');
INSERT INTO `user_info` VALUES (2, 'tank', '123');
INSERT INTO `user_info` VALUES (3, 'jerry', '123');
INSERT INTO `user_info` VALUES (4, 'egon', '456');

SET FOREIGN_KEY_CHECKS = 1;
配路由与视图函数

urls.py

from views import *

urls = [
    ('/index', index),
    ('/login', login),
    ('/new_url', new_url),
    ('/get_time', get_time),
    ('/show_dic', show_dic),
    ('/get_users', get_users),
]

views.py

def index(env):
    return 'index'


def login(env):
    return 'login'


def new_url(env):
    # 读取并把 new_url 文件返回给客户端(浏览器)
    with open(r'templates/new_url.html', 'rb') as f:
        html_data = f.read()
    return html_data


def get_time(env):
    # 读取并把 get_time 文件返回给客户端(浏览器)
    with open(r'templates/get_time.html', 'rb') as f:
        html_data = f.read().decode('utf-8')
    import time
    html_data = html_data.replace('put_time_here', time.strftime("%Y-%m-%d %X"))
    return html_data


def show_dic(env):
    user = {
        "username": "jason",
        "age": 18,
    }
    with open(r'templates/show_dic.html', 'rb') as f:
        html_data = f.read()

    # 使用 jinja2 的模板语法来将数据渲染到页面上(替换占位符)
    from jinja2 import Template
    tmp = Template(html_data)
    res = tmp.render(dic=user)  # 将字典 user 传递给前端页面,前端页面通过变量名 dic 就能够获取到该字典
    return res


# 先写个空函数在这里占位置,去把 pymysql 查数据的写了再过来完善
def get_users(env):
    # 从数据库取到数据
    import op_mysql
    user_list = op_mysql.get_users()

    with open(r'templates/get_users.html', 'r', encoding='utf-8') as f:
        html_data = f.read()

    from jinja2 import Template  # 其实这个引入应该放在页面最上方去的,但为了渐进式演示代码推进过程,就放在这里了
    tmp = Template(html_data)
    res = tmp.render(user_list=user_list)
    return res

op_mysql.py 如果你的配置不一样要自己改过来

import pymysql


def get_cursor():
    server = pymysql.connect(
        # 根据自己电脑上 mysql 的情况配置这一块的内容
        host='127.0.0.1',
        port=3306,
        user='root',
        password='000000',
        charset='utf8',  # 千万注意这里是 utf8 !
        database='django_test_db',
        autocommit=True
    )
    cursor = server.cursor(pymysql.cursors.DictCursor)
    return cursor


def get_users():
    cursor = get_cursor()  # 连接数据库

    sql = "select * from user_info"  # 把用户的所有信息查出来(一般不会把密码放回给前端的,这里只是为了做演示)
    affect_rows = cursor.execute(sql)
    user_list = cursor.fetchall()
    return user_list

templates/get_users.html 用户信息展示页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--  引入jquery bootstrap 文件的 CDN  -->
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
    <div class="container">
        <div class="row">

            <div class="col-md-8 col-md-offset-2">
                <h2 class="text-center">用户数据展示</h2>
                <table class="table table-hover table-bordered table-striped">
                    <thead>
                    <tr>
                        <th>id</th>
                        <th>username</th>
                        <th>password</th>
                    </tr>
                    </thead>
                    <tbody>
                    <!-- jinja2 的模版语法(for循环) -->
                    {%for user_dict in user_list%}
                    <tr>
                        <td>{{user_dict.id}}</td>
                        <td>{{user_dict.username}}</td>
                        <td>{{user_dict.password}}</td>
                    </tr>
                    {%endfor%}
                    </tbody>
                </table>
            </div>

        </div>
    </div>
</body>
</html>

用浏览器访问 http://127.0.0.1:8080/get_users,重启服务器,在切回浏览器即可看到页面效果

1725797-20190915011902104-829550490.png

推导流程与小总结

1.纯手撸web框架
    1.手动书写socket代码
    2.手动处理http数据
    
2.基于wsgiref模块帮助我们处理scoket以及http数据(顶掉上面的歩鄹)
    wsgiref模块
        1.请求来的时候 解析http数据帮你打包成一个字典传输给你 便于你操作各项数据
        2.响应走的时候 自动帮你把数据再打包成符合http协议格式的样子 再返回给前端
    
3.封装路由与视图函数对应关系 以及视图函数文件 网站用到的所有的html文件全部放在了templates文件夹下
    1.urls.py 路由与视图函数对应关系
    2.views.py 视图函数 (视图函数不单单指函数 也可以是类)
    3.templates 模板文件夹
    
4.基于jinja2实现模板的渲染
    模板的渲染
        后端生成好数据 通过某种方式传递给前端页面使用(前端页面可以基于模板语法更加快捷简便使用后端传过来的数据)

流程图

1725797-20190915011827519-2084620178.png

小扩展

在不知道是要 encode 还是 decode 的时候,可以用一下方法

二进制数据对应的肯定是 decode 解码 成字符串呀

字符串对应的肯定是 encode 编码成二进制数据呀

数据类型转换技巧(处理编码)(数据 + encoding)

# 转成 bytes 类型
bytes(data, encoding='utf-8')

# 转成 str 类型
str(data, encoding='utf-8')

python三大Web主流框架分析对比

Django

大而全,自带的功能特别特别多,就类似于航空母舰

缺点:有时过于笨重(小项目很多用不到)

Flask

短小精悍,自带的功能特别少,全都是依赖于第三方组件(模块)

第三方组件特别多 --> 如果把所有的第三方组件加起来,完全可以盖过django

缺点:比较受限于第三方的开发者(可能有bug等)

Tornado

天生的异步非阻塞框架,速度特别快,能够抗住高并发

​ 可以开发游戏服务器(但开发游戏,还是 C 和C++用的多,执行效率更快)

手撸三大部分在框架中的情况对比

前面的手撸推导过程,整个框架过程大致可以分为以下三部分

A:socket处理请求的接收与响应的发送

B:路由与视图函数

C:模板语法给动态页面渲染数据

Django

A:用的别人的 wsgiref 模块
B:自带路由与视图函数文件
C:自带一套模板语法

Flask

A:用的别人的werkzeug 模块(基于 wsgiref 封装的)
B:自带路由与视图函数文件
C:用的别人的jinja2

Tornado

A,B,C全都有自己的实现

Django的下载安装基本使用

参见我的另一篇博客:Django-下载安装-配置-创建django项目-三板斧简单使用

转载于:https://www.cnblogs.com/suwanbin/p/11520959.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值