wsgiref 与 Django框架初识
Web框架的本质
我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端,这样我们就可以自己实现Web框架
软件开发架构:
c/s架构 客户端与服务端
b/s结构 浏览器与服务端
半成品自定义Web架构
import socket server = socket.socket() # 不传参数默认TCP协议通信 # 绑定 IP PORT server.bind(('127.0.0.1', 8080)) # 服务端:1.固定的ip+port,2.24小时不间断服务,3.支持高并发 # 半连接池 server.listen(5) # 降低效率,保证服务端硬件的安全性 while True: conn, addr = server.accept() # 等待客户端连接 data = conn.recv(1024) # 接收客户端请求信息 conn.send(b'hello Django') # 向客户端响应信息,但响应失败,客户端无法接收,因为没有遵循HTTP协议
Web服务本质上都是这十几行代码基础上扩展出来的.
用户在浏览器输入网址:
浏览器发请求(get/post) <--> HTTP协议 <--> 服务端响应请求
HTTP协议规定了客户端与服务端消息的传世格式,那么我们就看看是怎样规定显式格式的,我们在服务端打印出接收到的信息是什么
import socket server = socket.socket() server.bind(('127.0.0.1', 8080)) server.listen(5) while True: conn, addr = server.accept() data = conn.recv(1024) print(data) # 打印客户端请求数据 conn.send(b'hello Django')
# 打印出来的请求数据
''' b'GET / HTTP/1.1\r\n Host: 127.0.0.1:8080\r\n Connection: keep-alive\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/75.0.3770.100 Safari/537.36\r\n Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3\r\n Accept-Encoding: gzip, deflate, br\r\n Accept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n \r\n ' '''
我们发现收发的消息需要按照一定的格式来,这里就需要了解一下HTTP协议了
HTTP协议对收发消息的格式要求
每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。 HTTP响应的Header中有一个 `Content-Type`表明响应的内容格式。如 `text/html`表示HTML网页。
处女版自定义Web框架
import socket server = socket.socket() server.bind(('127.0.0.1', 8080)) server.listen(5) while True: conn, addr = server.accept() data = conn.recv(1024) conn.send(b'HTTP/1.1 200 OK\r\n\r\nhello Django') conn.colse()
以上就是web 框架的本质。
接下来就就是完善我们的自定义web框架
根据不同的路径返回不同的内容
如何让我们的Web服务根据用户请求的URL不同而返回不同的内容呢?
我们发现,用户输入不同的url在服务端接收的请求是不一样的,区别在与
b'GET /index HTTP/1.1\r\nHost: 127.0.0.1:8080\r\n.....
里面多了/index,也就是用户输入的url后缀,那么我们就可以通过切分拿到/index后缀,在进行判断返回相应的响应就可以
import socket server = socket.socket() server.bind(('127.0.0.1', 8080)) server.listen(5) while True: conn, addr = server.accept() data = conn.recv(1024) conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # TCP 流式协议特性 data = data.decode('utf-8') current_path = data.split('\r\n')[0].split(' ')[1] # 拿到url后缀 if current_path == '/index': # 判断后缀,响应响应的功能 conn.send(b'index') elif current_path == '/text': with open(r'1html.html', 'rb') as f: conn.send(f.read()) else: conn.send(b'hello Django') conn.close()
wsgiref
from wsgiref.simple_server import make_server # wsgiref 将socket封装好了 def run(env, response): ''' env 是请求相关的数据 response 是响应相关的数据 ''' print(env) # 收到的是一个字典,里面的 键 PATN_INFO 对应的值就是客户端url的请求后缀 response('200 OK', []) return [b'hello Django'] if __name__ == '__main__': server = make_server('127.0.0.1', 8080, run) # 实时监测127.0.0.1:8080地址,一旦有客户端连接,会自动加括号调用run方法 server.serve_forever() # 启动服务端
根据不同的路径返回不同的内容--函数进阶版
from wsgiref.simple_server import make_server # wsgiref 将socket封装好了 def index(env): return 'index' def login(env): return 'login' def errors(env): return '404 error' urls = [ ('/index', index), ('/login', login), ] def run(env, response): response('200 OK', []) current_path = env.get('PATH_INFO')# 拿到的是URL后缀 # 定义一个存储函数的标志位 func = None for url in urls: # 判断当前请求的url是否在元组内 if url[0] == current_path: # 若在,赋值给func func = url[1] # 一旦匹配上就退出循环,节省资源 break # 判断func是否有值 if func: # 调用 res = func(env) # env是个大字典,里面含有信息,在后续的逻辑中可能还需要用到字典内的信息 else: res = errors(env) return [res.encode('utf-8')] # 将所有逻辑函数返回的字符串在此统一编码,发给客户端 if __name__ == '__main__': server = make_server('127.0.0.1', 8080, run) # 实时监测127.0.0.1:8080地址,一旦有客户端连接,会自动加括号调用run方法 server.serve_forever() # 启动服务端
基于wsgiref模块将代码拆分成urls.py路由文件,views.py视图文件和wsgiref通信文件三部分,后续加功能只需要在urls.py文件和view.py文件中加入对应功能即可
1.urls.py 路由与视图函数的对应关系
2.views.py 视图函数
让网页动态起来
静态网页:数据时写死的,万年不变
动态网页:如
1.后端实时获取当前时间'传递'给前端页面展示
2.后端从数据库获取数据'传递'给前端页面展示
ps : 传递即渲染
获取当前时间
from datetime import datetime def get_time(env): # 现在后端获取当前时间 current_time = datetime.now().strftime('%Y-%m-%d %X') # 将获取的时间添加到HTML代码中的思路: # 1.先在<body>内指定位置用一串字符占位($$time$$) # 2.文件的形式读取HTML页面,对于计算机来说,html页面只是一串二进制数据 # 3.用 r 模式对出来的是一串字符串,利用字符串替换,将拿到的时间和站位的字符串替换即可 with open(r'templates/2get_time.html', 'r', encoding='utf-8') as f: data = f.read() data = data.replace('$$time$$', current_time) return data
从数据库中查询数据
jinja2:
上面的代码实现了一个简单的动态,我完全可以从数据库中查询数据,然后去替换我html中的对应内容,然后再发送给浏览器完成渲染。
这个过程就相当于HTML模板渲染数据。 本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据。 我这里用的特殊符号是我定义的,其实模板渲染有个现成的工具: jinja2
from jinja2 import Template import pymysql def get_db(env): conn = pymysql.connect( host = '1270.0.1', port = 3306, user = 'root', password = 'root', database = 'db4', charset = 'utf8', autocommit = True, ) cursor = conn.cursor(pymysql.cursors.DictCursor) sql = "select * form userinfo" cursor.execute(sql) data = cursor.fetchall() with open(r'templates/4get_db.html', 'r', encoding='utf-8')as f: data_user = f.read() temp = Template(data_user) res = temp.render(user_list = data) return res
<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> {%for user_dict in user_list%} <tr> <td>{{user_dict.id}}</td> <td>{{user_dict.name}}</td> <td>{{user_dict.password}}</td> </tr> {%endfor%} </tbody> </table> </div> </div>
模板渲染
后端生成的数据直接传递给前端页面使用(并且前端页面可以灵活的操作改数据) >>> 模板语法
模板渲染 模板语法需要依赖于第三方模块
pip install jinja2
模板语法 jinja2支持前端直接使用类似于python的语法操作数据,语法如下:
取值:
<p>{{ user_dic }}</p> <p>{{ user_dic.name }}</p> <p>{{ user_dic['password'] }}</p> <p>{{ user_dic.get('name') }}</p>
for循环:
{% for user in user_dict %} <!--[{},{},{},{}]--> <tr> <td>{{ user.id }}</td> <td>{{ user.name }}</td> <td>{{ user.password }}</td> </tr> {% endfor %}
正戏 Django
python三大主流web框架:
1. Django:大而全,自带了很多功能模块,类似于航空母舰 (缺点:有点笨重)
2. Flask:短小精悍,自带的功能模块特别少,大部分都是依赖于第三方模块(小而轻)
3. Tornado:异步非阻塞 主要用在处理高io 多路复用的情况 可以写游戏后端
a:socket
b:路由与视图函数
c:模板渲染
Django:
a用的别人的 wsgiref
b自己写的
c自己写的
Flask:
a用的别人的 werkzeug
b自己写的
c用的别人的 jinja2
Tornado:
a,b,c都是自己写的
使用Django注意事项(*****************************):
1.计算机的名称不能有中文
2.一个pycharm窗口就是一个项目,不要多个项目放在一个窗口里面
3.项目名不能起中文
一.djang下载 推荐下载1.11.11版本
命令行直接下载
pip3 install django==1.11.11
pycharm下载
验证是否下载成功:
django-admin
二.创建django项目的方式
1.命令行创建
创建django项目: django-admin startproject 项目名 创建app应用(在项目名下): django-admin startapp 应用名 python3 manage.py startapp 应用名 启动django项目 python3 manage.py runserver ps:用命令行创建django默认不会自动创建templates文件夹 需要你手动自己创建(注意该文件夹路径是否被添加配置文件中) Ctrl+c 停止运行
2.pycharm创建
FILE >>> new project 选择第二个django 需要注意名字不能有中文,选择本地的解释器,勾选后台管理 创建app pycharm命令行创建 python3 manage.py startapp 应用名 Tools下面run manage task功能栏 startapp 应用名 启动点小绿色箭头
三.django各个文件的作用
1.应用名文件
migrations 数据库迁移记录相关数据
admin.py django后台管理相关
models.py 模型表相关
views.py 视图函数相关
apps.py 应用注册相关
texte.py 测试文件
views.py 视图函数
2.项目名文件
settings.py 配置文件
urls.py 路由与视图函数的映射关系
3.templates
项目用到的所有的html文件
4.manage.py
django入口文件
四.注意新创建的app需要在配置文件中注册才能生效(*******)
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', '应用名.apps.App01Config' # 可以用全称 '应用名' # 也可以简写
]
五.django小白必会三板斧
from django.shortcuts import render,HttpResponse,redirect
1.HttpResponse 返回字符串
return HttpResponse('字符串') # 例: def index(request): return HttpResponse('hello first django') # 返回字符串
2.render 返回HTML页面
return render() # 第一个参数request,第二个参数HTML,第三个参数字典 # 例: def login(request): user_dict = {'username':'haha'} return render(request, '01 login.html', {'data':user_dict}) # 返回html页面
3.redirect 重定向
def home(request): return redirect('/login/') # 重定向