引入web框架以及优化过程

简单的web框架搭建

软件开发目录规范

  1. C/S架构
  2. B/S架构

ps: B/S架构的本质就是一个C/S结构

搭建web框架

我们都知道如果我们想开发一个B/S的架构的软件,就必须要依赖于浏览器.

而为了让我们的页面能够被浏览器识别,我们就必须得遵循别人的协议标准.

当然你也可以不遵循,但是你得自己开发一个客户端,用于你自己的服务器上,要是你这个需求量也很大,别人都要下载你的客户端程序,而不是浏览器,当然它们就可以遵循你的标准协议使用了.

那么我们就可以想到有一个现成的客户端,并且功能还那么强大,我们就没必要重复造轮子了.

所有我们就基于浏览器去开发服务器就好了.

那么这个浏览器的协议是什么呢??

HTTP协议

我们就从http协议开始入手

http协议有四大特点

  1. 它是基于TCP/IP协议中的应用层,是在我们的应用程序中的协议

  2. 它是基于请求响应的协议

    什么意思?

    我们要知道我们的浏览器是一个“超级”客户端,在浏览器的网址栏中输入一个url就是一个请求,而服务端收到请求就会有一个响应去回复浏览器的请求信息.

    都是客户端去和服务端发送请求的

  3. 无状态的

    就是它不会记住你的服务器状态,你每一次来它都当作你是第一次来,并不会因为你来了几次就记住你

    就好比说纵然见你千百遍,我却始终待你如初见.

    这可得讲讲它的历史了,以前的浏览器啊,里面打开一个网页就是一写文章和报纸一样的,但是随着互联网慢慢的发展,报纸格式也就是说全文字的网页以及满足不了用户了,就变成了现在的支付宝啊,淘宝啊.这个在我们使用的过程中.我们是不是登录了下次就不用登录啦.你淘宝里面的购物车信息,下次登录还在.还有那个扫描二维码登录的.

    这都是为了解决无连接的.我们会用到cookie,session.和taken技术了.这都可以起到保存我们的用户状态的作用.

  4. 短链接&无链接

    就是每次浏览器和服务器连接之后,默认都是交互完毕之后就自动断开连接了.所有我们每次操作浏览器都会有很多的次的断开连接,再断再连的操作过程.

    而还有一种就是长连接:

    顾名思义,长连接就是一个默认不会断开俩者之间连接的.一般可以基于websocket实现.这个实现的还可以让服务器端给你浏览器发送请求. 就比如一写小网站些弹出广告啊,那些的.

那了解了http的四大特性,里面什么是请求什么是响应?

什么是响应

请求就是浏览器向服务器发送get/post请求,接收/上传 服务器的数据

响应就是服务器接收到客户端的请求信息,给予的回应,比如返回 一个 200 ok 的页面信息

我们再讲讲http的组成,为了方便管理和处理,我们的请求格式和响应格式组成的方式是差不多一样的.

请求格式

  1. 请求首行: =====> http的版本信息 和请求方式
  2. 请求头部 ====> 放的是一堆客户端的信息字典,一大堆的k,v键值对
  3. /r/n/
  4. 请求体 (并不是所有的请求方式都有,get没有post有,post存放的是一些提交的敏感数据,比如说账号和密码)

请求格式图解

img

http 响应格式

响应首行 ====> 标识http协议的版本, 响应状态码

响应头 ===== > 一大堆k,v键值对

/r/n

响应体 =====> 返回给浏览器展示给用户看的数据.

响应格式图解:

img

介绍网络名词

请求方式:

1.get请求

  • 朝服务器要数据
  • eg:输入url获取对应的数据

2.post请求

  • 朝服务器提交数据
  • eg: 用户登录,向服务器提交用户名和密码数据 后端接收到数据就那些校验

3.get请求和post请求的区别

  • get请求发送的数据是在url中携带的,以?分割url和数据,并且数据与数据之间以&符号链接,所以是暴露在网页中的

  • post请求发送的数据是在一个data的字典对象数据包里面的中,是另外存储的,并且是有加密手段的,更加的安全

    • eg:
  • get请求不如post请求安全,但一般的网络请求都是get.post一般用于敏感数据.

url

定义为: 统一资源定位符

格式:scheme:[//[user:password@]host:[:port][/]path[?query-string][#anchor]

参数介绍:

  • scheme:协议(例如:http/https/ftp.等)
  • user:password@ 用户的登录名和密码
  • host:服务器的ip地址或者域名
  • port: 服务器的端口号 (如果用的是协议,比如http(默认端口80),https(默认端口为443))
  • path: 访问资源的路径
  • query-string: 参数,一般为客户端发送给服务端的数据
  • anchor:锚(跳转到指定的锚点位置)

例如:url: https://www.cnblogs.com/jkeykey/p/14521413.html

对应关系

  • scheme: https
  • host:www.cnblogs.com
  • port: 443
  • path: /jkeyjkey/p/14521413.html

开始推理web框架

介绍了http的相关知识,现在我们终于可以搭建我们的web框架了

未遵循浏览器标准

未遵循浏览器标准的代码如下:

mport socket

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)


while True:
    conn, client_addr = server.accept()
    data_bytes = conn.recv(8192)
    print(data_bytes)  # 将浏览器发来的消息打印出来
    """
    ===================== HTTP协议的请求数据格式4部份 =====================
    b'GET / HTTP/1.1
       # 请求首行(标识HTTP协议版本,当前请求方式)
    Host: 127.0.0.1:8080
   # 请求头(一大堆k,v键值对)
    Connection: keep-alive

    Upgrade-Insecure-Requests: 1

    User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 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,en;q=0.8

    
'      # 

               # 请求体(并不是所有的请求方式都有. get没有post有, post存放的是请求提交的敏感数据)
               
    b''               
    """
    conn.send(b'ok')
    conn.close()

我们这样如果是用自己的客户端,发送回收打印是可以的.但是如果在浏览器中打开,是查看不到ok这个字段的.

那如何才能在浏览器上显示我们的内容,或者说html文件呢?

这就要看看浏览器的格式了.

我们以访问博客园服务器为例

我们访问: https://www.cnblogs.com/

右键检查,点击network,然后刷新一下页面,抓取到网页的数据包信息

响应相关信息可以在浏览器调试窗口的network标签页中看到。

img

点击view source之后显示如下图:

img

那我们知道了,响应格式

遵循http协议

那我们的代码就应该也要遵循他的协议

代码改成

import socket

server = socket.socket()

server.bind(("127.0.0.1", 8080))

server.listen(5)


while True:

    conn,addr = server.accept()

    data_bytes = conn.recv(1024)
    print(data_bytes)
	
    # 先发送我们的格式的首行信息和 相应状态 以及换行.
    # 为什么是俩个 
,那是因为第一个
是状态码的换行,第二个才是与代码体的换行
    conn.send(b"http/1.1 200 ok 

")
    conn.send(b"hello world")

    conn.close()

客户端访问的数据格式

b'GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Pragma: no-cache

Cache-Control: no-cache

sec-ch-ua: "Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"

sec-ch-ua-mobile: ?0

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9

Sec-Fetch-Site: none

Sec-Fetch-Mode: navigate

Sec-Fetch-User: ?1

Sec-Fetch-Dest: document

Accept-Encoding: gzip, deflate, br

Accept-Language: zh-CN,zh;q=0.9


'

此时我们访问的页面,就可以看到我们的 hello word 信息了

web框架之显示html文件

然后现在我们就可以对我们的服务端进行优化了

现在有一个需求,让我们模拟,百度冲浪,输入一个url就可以获取到对应的页面内容

那这要怎么实现呢?

我们可以先拿到客户端数据中的url中的页面信息

代码演示

import socket

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)

while True:
    conn, client_addr = server.accept()
    data_bytes = conn.recv(8192)
    print(data_bytes)
    '''
    b'GET /index HTTP/1.1
Host: 127.0.0.1:8080
C
    br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

    
'
    '''
    path = data_bytes.split()[1]
    print(path)   # b'/index'
    conn.send(b"HTTP/1.1 200 OK

")   # 因为要遵循HTTP协议,所以回复的消息也要加状态行
    if path == b'/index':
        response = b'you can you do not bb: %s' % path
    elif path == b'/login':
        response = b'you can you not bb: %s' % path
    else:
        response = b'404	Not Found'
    conn.send(response)
    conn.close()

访问index和login时

访问一个不存在的url时

优化web框架之urls_list

那么这时候,问题又来了. 一个url就要对应一个if分支,那么我如果有100个呢?

那么要写100j个if判断吗??这肯定是不行的,所以我们要通过字典或者列表来优雅的替代多分支

代码如下

import socket

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)


# 将返回不同的内容部分封装成函数
def index(path):
    s = "This is {} page table!".format(path)
    return s


def login(path):
    s = "This is {} page table!".format(path)
    return s


def error(path):
    return '404 not found: {}'.format(path)


# 定义一个url和实际要执行的函数的对应关系
urls = [
    ('/index', index),
    ('/login', login),
]

while True:
    conn, client_addr = server.accept()
    data_bytes = conn.recv(8192)
    print(data_bytes)
    path = data_bytes.split()[1].decode('utf-8')  # 分离出的访问路径, 再把收到的字节类型的数据转换成字符串
    print(path)  # '/index'
    conn.send(b"HTTP/1.1 200 OK

")  # 因为要遵循HTTP协议,所以回复的消息也要加状态行

    func = None  # 定义一个保存将要执行的函数名的变量
    for url in urls:
        if path == url[0]:
            func = url[1]
            break
    if func:
        response = func(path)
    else:
        response = error(path)

    # 返回具体的响应消息
    conn.send(response.encode('utf-8'))
    print(response.encode('utf-8'))
    conn.close()

后缀/index路径访问

后缀/login

路径不存在时

知识点

动静态网页

静态网页

静态网页就是我们之前用的前端的知识搭建的,内容是写死的,比如<p>111</p>

用这样的标签搭建的网页就是静态网页.

那么动态网页就是和这个相反的,标签内的内容是,即是后端获取的, 比如 <p>{{ name }}</p>,用这样的标签搭建的网页就是动态网页.

最大的区别就是,静态的网页,不改文本的内容是不会变的,而动态页面可以通过改后端的数据来改变网页中的内容.

静态网页详细介绍: 返回具体的html文件

我们之前用渲染给客户端的数据都是str字符串类型的,那我们怎么讲html文件中的内容渲染到浏览器上呢?

一样的思想,可以先将html文件中的内容全部拿到,一起发送给浏览器,让其渲染网页即可

代码为

import socket

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)

def get_f(path):
    import os
    path = r"./templates"+path+".html"
    with open(path,'rb') as f:
        html_data = f.read()
        return html_data

# 将返回不同的内容部分封装成函数
def index(path):
    s = get_f(path)
    return s


def login(path):
    s = get_f(path)
    return s


def error(path):
    return bytes('404 not found: {}'.format(path).encode("utf-8"))


# 定义一个url和实际要执行的函数的对应关系
urls = [
    ('/index', index),
    ('/login', login),
]

while True:
    conn, client_addr = server.accept()
    data_bytes = conn.recv(8192)
    print(data_bytes)
    path = data_bytes.split()[1].decode('utf-8')  # 分离出的访问路径, 再把收到的字节类型的数据转换成字符串
    print(path)  # '/index'
    conn.send(b"HTTP/1.1 200 OK

")  # 因为要遵循HTTP协议,所以回复的消息也要加状态行

    func = None  # 定义一个保存将要执行的函数名的变量
    for url in urls:
        if path == url[0]:
            func = url[1]
            break
    if func:
        response = func(path)
    else:
        response = error(path)

    # 返回具体的响应消息
    conn.send(response)
    conn.close()

当为index时

当为login时

动态网页

动态网页: 后端获取当前的时间展示到index.html页面上

代码如下

import socket

server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)


# 将返回不同的内容部分封装成函数
def _open_read(filename):
    with open(filename, 'rt', encoding='utf-8') as f:
        return f.read()


def index(path):
    return _open_read(r'.	emplatesindex.html')


from datetime import datetime
def login(path):   # !!!动态网页!!: 后端获取当前时间展示到html页面上
    data = _open_read(r'.	emplateslogin.html')
    data = data.replace('login', "login" + datetime.now().strftime('%Y-%m-%y %X'))
    return data


def error(path):
    return _open_read(r'.	emplateserror.html')


# 定义一个url和实际要执行的函数的对应关系
urls = [
    ('/index', index),
    ('/login', login),
]

while True:
    conn, client_addr = server.accept()
    data_bytes = conn.recv(8192)
    print(data_bytes)
    path = data_bytes.split()[1].decode('utf-8')  # 分离出的访问路径, 再把收到的字节类型的数据转换成字符串
    print(path)  # '/index'
    conn.send(b"HTTP/1.1 200 OK

")  # 因为要遵循HTTP协议,所以回复的消息也要加状态行

    func = None  # 定义一个保存将要执行的函数名的变量
    for url in urls:
        if path == url[0]:
            func = url[1]
            break
    if func:
        response = func(path)
    else:
        response = error(path)

    # 返回具体的响应消息
    conn.sendall(response.encode('utf-8'))
    print(response.encode('utf-8'))
    conn.close()

优化web框架之wsgiref模块

使用wsgiref模块代替之前 web框架的socket server 部分

我们之前写的简单的web框架有俩个缺陷

  1. 代码重复

    服务端socket代码需要我们自己写.如果好多服务端就要写好多个socket模块的服务端

  2. http格式的数据得自己处理

    并且只能拿到url后缀 其他数据获取得通过re正则去header那个字符串里面取,太麻烦了.

而利用wsgiref模块优势

  1. wsgiref模块帮助我们封装了socket代码
  2. 帮我们处理http格式的数据

wsgiref模块就是一个web服务端网关接口"

  1. 请求来的时候会帮助你自动拆分http格式数据并封装成非常方便处理的数据格式 --->env大字典
  2. 响应走的时候会帮你将数据再打包成符合http格式的数据.

ps: 所以wsgiref模块会为我们做服务器对客户端的请求和响应数据,就不需要我们指定编码和解码了.而且取数据很方便

我们之前的代码都是写在一个文件内,我们都知道这是不允许的,所以我们应该对这些方法和代码进行区分,分文件存.

我们总结一下,我们之前的功能除了服务器发送还有三个

1.urls_list 一个url后缀和函数(类也可以)对应关系

2.views url对应的函数(类)的函数书写

3.templates 模块.存放一些html文件

所以我们现在可以讲这三个功能查分开来

这样做的好处是解耦合,拓展方便,只要按照 urls.py---> view.py ---->templates文件夹下的html 就可以搭建我们的web了.

现在我们的服务端启动文件可以为

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


def run_server(request, response):
    """
    函数定义什么都无所谓
    :param request: 请求相关的所有数据
                    是一个大字典,wsgires模块帮你处理好http格式的数据,封装成一个字典方便你的后续操作
    :param response: 响应相关的所有数据
    :return: 返回给浏览器的数据,返回的格式必须为一个二进制格式
    """
    response("200 OK", [])  # 响应首行 响应头
    print(request)
    """
    字典的其中一部分
    'PATH_INFO': '/login', 'QUERY_STRING': '', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'text/plain', 'HTTP_HOST': '127.0.0.1:8080'
    """
    path_info = request.get("PATH_INFO")
    print(path_info)

    func = None
    for url in urls_list:
        if path_info == url[0]:
            func = url[1]
            break
    if func:
        data = func(request)
    else:
        data = error(request)

    return [data.encode("utf-8")]


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

urls.py

"""
url后缀对应函数的对应关系文件
"""

from views import *


urls_list = [
    ("/index", index),
    ("/login", login),

]

views.py

"""
函数脚本,处理url对应的函数
"""


def _open_read(filename):
    with open(filename, 'rt', encoding='utf-8') as f:
        return f.read()


def index(request):
    return _open_read('templates/index.html')


def login(request):  # !!!动态网页!!: 后端获取当前时间展示到html页面上
    from datetime import datetime
    data = _open_read('templates/login.html')
    data = data.replace('login', datetime.now().strftime('%Y-%m-%y %X'))
    return data


def error(request):
    return _open_read('templates/error.html')

优化方案之jinja2模块

到现在我们使用的网页大部分都是静态网页,动态网页也是通过python的方法

那我们优化这种局部的渲染的.

那就要讲到另外一个模块了jinja2,

jinja2作用: jiaja2的原理就是字符串替换,我们只要在html页面中遵循jinja2的语法规范写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容效果.

要使用这个模块,我们得事先安装,安装了flask的不用安装,flask自动携带jinja2,默认使用的就是这个.

安装命令:通过清华的地址,下载会快一点

pip3 install -ihttps://pypi.tuna.tsinghua.edu.cn/simple jinja2

jinja2的模板语法

// 定义变量使用双花括号
{{ 变量名称 }}  // 该变量可以是python内的所有数据类型

// 执行for循环,if流程控制 为花括号内左右%
{% for i in list %}
{{ i.id }}  # 不仅支持python的数据类型的语法,还支持点类属性取值的方法
{% endfor %}

那我们将这个运用到我们的框架中

使用jinja2优化网页内容

例子: 将user表中的数据渲染到 user.html网页中

创建user表

create database db777  charset utf8;
use db777
create table user(
	id int primary key auto_increment,
    username varchar(16),
    pwd varchar(14),
    sex enum("男","女"),
    hobbies set("吃饭","睡觉","打土匪","打劫")
); 
insert into user(username,pwd,sex,hobbies) values
("jkey","123","男","吃饭,打土匪"),
("土匪","123","男","吃饭,打劫"),
 ("派大星","123","男","打土匪");

执行文件不需要更改

所以执行文件用上面的即可

urls.py

"""
url后缀对应函数的对应关系文件
"""

from views import *


urls_list = [
    ("/index", index),
    ("/login", login),
    ("/user", user)
]

views.py

"""
函数脚本,处理url对应的函数
"""


def _open_read(filename):
    with open(filename, 'rt', encoding='utf-8') as f:
        return f.read()


def index(request):
    return _open_read('templates/index.html')


def login(request):  # !!!动态网页!!: 后端获取当前时间展示到html页面上
    from datetime import datetime
    data = _open_read('templates/login.html')
    data = data.replace('login', datetime.now().strftime('%Y-%m-%y %X'))
    return data


def _open_mysql():
    import pymysql
    conn = pymysql.connect(
        host="127.0.0.1",
        port=3306,
        user="root",
        passwd="jzd123",
        db="db777",
        charset='utf8',
    )
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    affected_rows = cursor.execute("select * from user;")
    if affected_rows:
        user_dict_list = cursor.fetchall()
        return user_dict_list
    else:
        print("对不起你要查找的数据不存在")
    cursor.close()
    conn.close()


def user(request):
    from jinja2 import Template
    data = _open_read("templates/user.html")
    user_dict_list = _open_mysql()
    if user_dict_list:
        temp = Template(data)
        res = temp.render(user_dict_list=user_dict_list)
        return res
    return error(request)


def error(request):
    return _open_read('templates/error.html')

templates/user.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <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>
    <title>Title</title>
</head>
<body>
<div class="container">
    <div class="row">
        <h1 class="text-center">用户表信息</h1>
        <table class="table table-hover table-striped table-bordered">
            <thead>
            <tr>
                <th>序号</th>
                <th>名称</th>
                <th>密码</th>
                <th>性别</th>
                <th>爱好</th>
            </tr>
            </thead>
            <tbody>
            {% for user_dict in user_dict_list %}
            <tr>
                <td> {{user_dict.id}}</td>
                <td> {{user_dict.username}}</td>
                <td> {{user_dict.pwd}}</td>
                <td> {{user_dict.sex}}</td>
                <td> {{user_dict.hobbies}}</td>
            </tr>
            {% endfor %}
            </tbody>
        </table>
    </div>
</div>
</body>
</html>

ps:注意,浏览器只会识别html和css以及JavaScript,这个模板解析是在python后端处理好的,变成一个html然后再发送给浏览器渲染的,不是浏览器帮我们解析的模块语法.

致此我们的web简单的框架搭建就已经推理完毕了,这个过程最重要的就是对知识点的学习,以及整个的推导过程,代码不是很重要.重要的是推理过程.

思想一定要想,学习起来才快

总结

1.自定义简易版本web框架请求流程图

img

服务端开启使用wsgiref模块搭建的启动文件,一般配置了就不需要改变.

所以我们需要修改的文件以及步骤为 urls.py,views.py,templates模板下的html文件.

其中需要注意的是views.py中获取数据库的数据,渲染到html文件中,可以传参进去,可以是任何的python数据类型,模板语法也需要注意.

一定要敲,才能发现自己的薄弱点.

流程图流程

浏览器客户端.

wsgiref模块

  • 请求来: 处理浏览器请求,解析浏览器http格式的数据,封装成大字典(PATH_INFO中存放的就是url的访问资源的路径)
  • 相应去: 将数据打包成符合http协议的格式,再返回给浏览器

后端

  • urls.py

    ---> 找用户输入的路径有没有与视图函数对应关系的,有就去views.py内找到对应的函数

  • views.py

    功能1(静态): 视图函数找templates中的html文件,返回给wsgiref做http格式的封包处理,再返回给浏览器.

    功能2(动态):视图函数通过pymysql链接数据库,通过jinja2模板语法将数据库中的数据在templates文件夹下的html文件做一个数据的动态渲染, 最后返回给wsgiref做HTTP格式的封包处理, 再返回给浏览器.

    功能3(动态): 也可以通过jinja2模板语法对tmpelates文件夹下的html文件进行数据的动态渲染, 渲染完毕, 再经过wsgiref做HTTP格式的封包处理, 再返回给浏览器.

基本使用流程

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

    def run_server(request, response):
            """
            函数名定义什么都无所谓, 我们这里就用run_server.
            :param request:  请求相关的所有数据.
                是一个大字典, 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 视图函数(request):
        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模块:后端与http文件交互,本质是一种替换操作
    	from jinja2 import Template
        temp = Template(data)  # data为要操作的替换的数据
        retuen temp.render(user=user_date)  #user 是传入给html页面的操作变量
    
# templates 文件夹:管理html文件
	html中使用jinja2模块的语法:
        定义变量: {{  user }}
        for循环/if流程 :  {% for i in user %} ... {% endfor %}
            如果i是一个对象,那么可以通过i.xx或者i[xx]来获取值.
原文地址:https://www.cnblogs.com/jkeykey/p/14539945.html