首页 > 技术文章 > Django推导Django内部模块:wsgiref与jinja2

garyhtml 2022-02-23 20:07 原文

img

Django框架引导:推导过程

# 在了解Django框架之前先来研究一些其他的知识点,进而推导出什么是Django框架。

web应用

# 什么是web应用:
	应用:我们通常将我们计算机的软件称为应用,像qq,微信,爱奇艺等,这些都称之为应用
	web应用:只要是通过浏览器访问的应用都是web应用。
    # (比如:通过浏览器访问爱奇艺,通过浏览器访问淘宝。)

# 软件开发架构:
	服务端需要具备的特征:24小时对外提供服务
    C/S:client:客户端  server:服务端
    B/S:browser:浏览器 server:服务端
    # 本质上B/S架构也是C/S架构
    
# Web应用程序的优点:
	1. 无需将客户端软件下载安装到本地,只需要一个浏览器(百度 谷歌..)即可
    2. 节省资源
    3. 客户端不需要主动更新,服务端更新即可。
    
# Web应用程序的缺点:
	1. 一旦服务端出现的问题,那么客户端将立马收到影响
    2. 兼容性问题(在不同的浏览器可能会产生不同的影响) # 比如IE浏览器

那么作为程序员的我们web应用我们应该怎么部署呢?我们来一步步的探讨

手撸web框架

补充:

http的默认端口号:80    # 数据传输是明文的
https的默认端口号:443  # 数据传输是密文的
# web框架可以理解为服务端 现在我们自己手动写一个web服务端
import socket


server = socket.socket()  #创建socket模块 默认就是基于网络的TCP传输协议
server.bind(('127.0.0.1',8080))  # 绑定ip和port  127.0.0.1为本地回环地址
server.listen(5)  # 半连接池    


while True:
    conn, addr = server.accept()  # 与客户端建立连接 等待接听
    data = conn.recv(1024)    # 接受客户端数据指定最大为1024字节
    data = data.decode('utf8')  # 解码转成字符类型(变为字符串)
    conn.send(b'HTTP/1.1/ 200 OK\r\n\r\n')  # 通过浏览器访问就必须遵循http协议,写在这里是通过tcp的流式协议来与后面的内容做匹配
    
    # 如何做到后缀的不同返回不同的内容:
    # data拿到的是用户请求的所有信息(那么用户输入的后缀就在以空格分割的第二个位置)
    suffix_path = data.split(' ')[1]  # 以空格切割字符串并取到索引1 索引1及为url的后缀
    if suffix_path == '/index':     # 后缀为index时给客户端传输index
        conn.send(b'index')
        
    elif suffix_path == '/login':   # 后缀为login时给客户端传输login
        conn.send(b'login')
    else:   # 在无后缀的情况
        with open(r'myhtml.html','rb') as f:  # 同样可以导入一个html文件并传输实现手撸web框架
            conn.send(f.read())   # 给客户端返回一个html页面
    conn.close()  # 交互结束

弊端:

# 我们自己手撸的web框架的不足之处
1、 代码重复(在没写一个web服务端的时候服务端代码都要重复的写(创建链接等等))
2、 手动处理http格式的数据,并且只能拿到url后缀,其他数据获取繁琐(想要获取其他数据处理方式也大致一样,但是这样也就出现了重复的问题)
3、并发的问题(无法同时接收多个客户的请求)


# 那么怎么解决这些问题呢这就要用到wsgiref模块来帮助我们实现(让别人帮助我们撸)

wsgiref基本使用

WSGI(Web Server Gateway Interface 规范)

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

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

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

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

wsgiref模块:

Python标准库提供的独立WSGI服务器叫wsgiref (主要要用开发测试使用,效率较低)

# 在看wsgiref模块之前我们来自己手动写一个web框架来对比看一下效果

wsgiref模块基本使用

启动文件run.py代码块

from wsgiref.simple_server import make_server
from urls import urls   # 导入urls.py
from views import *


# env是wsgiref模块处理好的http格式的数据,封装成了字典让用户更加方便的操作
def run(env,response):  # env:请求相关的所有数据 response响应相关的数据 return :返回给浏览器的数据
    response('200 OK', [])   # 响应首行和响应头 返回的格式
    current_path = env.get('PATH_INFO')  # 从env中可根据PATH_INFO找到url后缀
    func = None   # 定义一个变量,存储匹配到的函数名
    for url in urls:
        if current_path == url[0]:  # 去索引0拿到的是元组的第一个元素
            func = url[1]   # 将url对应的函数名赋值给func
            break  # 匹配到一个就结束循环(节省内存)
    if func:  # 如果func有值说明匹配到了 然后执行对应的函数 并且把env(用户请求的相关信息)传参过去用以函数做处理
        res = func(env)
    else:  # 如果没有匹配到说明没有写对应后缀的逻辑代码块
        res = error(env)  # 返回一个报错页面或者跳转页面给用户。
    return [res.encode('utf8')]  # 统一编码函数的返回值 并返回给用户

if __name__ == '__main__':
    server = make_server('127.0.0.1',8080,run)   # 实时监听(127.0.0.1:8080) 只要有客户端来(无论有什么后缀) 触发run函数运行run(env,response)并且给run函数传了两个参数(env,response) ,如果放的不是函数放的是对象,那么同样会给对象()执行产生对象类里面的__call__方法
    server.serve_forever()   # 启动服务端

# 这个里面的功能就不需要更改了,如果有新的功能只需要写下述代码块就可以(这样写不会是代码冗余,每一个py文件就写一个功能。)

urls.py代码块

# 存储路由(url后缀)与视图函数对应(主要的后端业务逻辑)关系

from views import *

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

views.py代码块

# 该板块存储主要的业务逻辑


def index(env):
    return 'index'

def login(env):
    return 'login'

def error(env):
    return '404 error'

def xxx(env):  # 同样可以返回html文件
    with open(r'templates/myhtml.html', 'r', encoding='utf8') as f:
        return f.read()

import datetime
def gettime(env):
    current_time = datetime.datetime.now().strftime('%Y-%m-%d %X')
    # 如何将后端获取到的时间数据传递给html文件
    with open(r'templates/mytime.html','r',encoding='utf8') as f:
        data = f.read()
        data = data.replace('time',current_time)  # 在后端处理好数据后返回给前端
        return data

总结:

urls.py			# 存储路由与视图函数对应关系
views.py		# 存储视图函数(后端业务逻辑)
templates文件夹  # 专门用来存储html文件

# 按照功能的不同拆分之后,后续添加功能只需要
# 在urls.py书写对应关系
# 在views.py书写主要的业务逻辑即可

动静态网页

# 静态网页:
	页面上的数据是直接写死的,在人为不去后台修改的情况下,永远不会改变。

# 动态网页:
比如:
	1.后端获取当前时间展示到html页面上
    2.数据是从数据库中获取的展示到html页面上
    # 这些需要实施获取的都属于动态网页

动态页面示例:

urls.py

urls = [('/gettime',gettime)]

views.py

import datetime
def gettime(env):
    current_time = datetime.datetime.now().strftime('%Y-%m-%d %X')  # 
    # 如何将后端获取到的时间数据传递给html文件
    with open(r'templates/mytime.html','r',encoding='utf8') as f:
        data = f.read()
        data = data.replace('time',current_time)  # 在后端处理好数据(替换字符串)后返回给前端
        return data

mytime.html

<h1>动态模块</h1>
time

给html文件添加字典操作:

# 需求:将一个字典传递给html文件 并且可以在文件上方便快捷的操作字典数据

这里需要用到一个'Jinja2'模块:jinja2为第三方模块需要手动下载

urls.py

urls = [('/getdict',get_dict)]

views.py

from jinja2 import Template
def get_dict(env):
    user_dic = {'username':'gary','age':19,'hobby':'basketball'}
    with open(r'templates/get_dict.html','r',encoding='utf8') as f:
        data = f.read()
    tmp = Template(data)
    res = tmp.render(user=user_dic)   # 给html页面传递了一个值,页面上可通过变量名user就能够拿到user_dict
    return res

get_html.html

<body>
<h1>我是一个可操作字典</h1>
    {{ user }}   <!--jinja2提供的模板语法(非常贴近python语法)是在后端起作用的-->
<p>
    {{ user.get('username') }}   <!--可使用字典的方法取值-->
    {{ user.age }}
    {{ user['hobby'] }}
</p>
</body>

后端获取数据库中的数据展示到前端页面

urls.py

urls = [('/getuser',get_user)]

views.py

import pymysql
def get_user(env):
    # 去数据库中获取数据 传递给
    conn = pymysql.connect(
        host='127.0.0.1',
        port = 3306,
        user = 'root',
        password= '123',
        db='user',
        charset='utf8',
        autocommit =True
    )
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    sql = 'select * from userinfo'
    affect_rows = cursor.execute(sql)
    data_list = cursor.fetchall()
    # 将获取到的数据传递给html文件
    with open(r'templates/get_data.html','r',encoding='utf8') as f:
        data = f.read()
    tmp = Template(data)
    res = tmp.render(user_list = data_list)
    return res
#

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">
    <div class="col-md-8 col-md-offset-2">
      <h1 class="text-center">用户数据</h1>
      <table class="table table-hover table-striped">
        <thead>
          <tr>
            <th>ID</th>
            <th>username</th>
            <th>age</th>
            <th>hobby</th>
          </tr>
        </thead>
        <tbody>
          {% for user_dict in user_list%}
            <tr>
              <td>{{ user_dict.id }}</td>
              <td>{{ user_dict.username}}</td>
              <td>{{ user_dict.age}}</td>
              <td>{{ user_dict.hobby}}</td>
              
            </tr>
          {% endfor%}
        </tbody>
      </table>
    </div>
  </div>
</div>
</body>
</html>

上述我们自己使用不同的模块来推导出来的web框架流程有现成的别人写好的框架来供我们使用:我们只需要在固定的位置编写固定的代码即可

推荐阅读