纯手撸web框架、wsgiref模块下的手撸框架、jinja2模版语法、django框架简介、下载、基本操作等
纯手撸web框架
web框架可以简单的理解为是基于互联网的web服务端,即socket服务端。
之前我们也学过,想要浏览器与服务端进行交互,服务端必须基于http、ftp、https等协议发送数据给浏览器
结合如上,我们可以纯手撸出一个简易的web框架
import socket
server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
sock,address=server.accept()
data=sock.recv(1024)
print(data) # 通过打印key看出如果想要实现不同网址后缀显示页面的话需要对data进行切割,将第二个数据进行比对
sock.send(b'HTTP/1.1 200 OK\r\n\r\n')
if data.decode('utf8').split(' ')[1]=='/login':
sock.send(b'login view')
elif data.decode('utf8').split(' ')[1]=='/reg':
sock.send(b'reg view')
elif data.decode('utf8').split(' ')[1]=='/home':
sock.send(b'home view')
else:
sock.send(b'404 error')
上述代码存在以下几个问题:
- socket代码重复编写(应该封装好)
- 针对请求数据格式处理的复杂并且重复(过多if分支不符合项目需求)
基于wsgiref模块的简易web框架
基于wsgiref初始版本
from wsgiref.simple_server import make_server
def run(request,response):
"""
:param request: 请求数据
:param response: 响应数据
:return: 返回给浏览器的数据
"""
print(request)
response('200 OK', []) # 固定编写
if request.get('PATH_INFO')=='/login':
return [b'login html']
elif request.get('PATH_INFO')=='/reg':
return [b'reg html']
elif request.get('PATH_INFO')=='/home':
return [b'home html']
else:
return [b'404 error']
if __name__ == '__main__':
server=make_server('127.0.0.1',8080,run) # 任何访问127.0.0.1:8080的请求都会发给第三个参数加括号调用
server.serve_forever() # 永久启动
此简易版本也没有解决if分支过多问题,我们改进一下
- 因为request请求体被wsgiref封装成了字典形式,路径为
PATH_INFO
的value - 将路径和功能函数以元组或列表的形式封装起来,做好对应关系
- 写好每个请求路径返回页面的功能函数
- 判断浏览器请求的路径是否在urls中,并处理请求。
基于wsgiref框架改进版本
from wsgiref.simple_server import make_server
def login(request):
return 'login html'
def reg(request):
return 'reg html'
def home(request):
with open('home.html','rb') as f:
return f.read()
def error(request):
return '404 error'
urls=(('/login',login),('/reg',reg),('/home',home),('/error',error))
def run(request,response):
"""
:param request: 请求数据
:param response: 响应数据
:return: 返回给浏览器的数据
"""
print(request)
response('200 OK', []) # 固定编写
# for url in urls:
# if request.get('PATH_INFO')==url[0]:
# res=url[1](request)
# return [res.encode('utf8')]
# return [error(request).encode('utf8')]
func_name=None
for url in urls:
if request.get('PATH_INFO')==url[0]:
func_name=url[1]
if func_name:
return [func_name().encode('utf8')]
return [error(request).encode('utf8')]
if __name__ == '__main__':
server=make_server('127.0.0.1',8080,run) # 任何访问127.0.0.1:8080的请求都会发给第三个参数加括号调用
server.serve_forever() # 永久启动
我们也可以将功能函数、对应关系、启动文件分成各个py文件
- 功能函数: views.py
- 对应关系: urls.py
- 启动文件: start.py
- html页面: templates文件夹
这样,一个真正的简易版框架就做好了。
动静态网页
动态网页
页面上的数据不是写死的,而是通过动态获取(后端传入)
静态网页
页面上的数据是写死的,要想修改只能修改源码
实际需求
后端代码写出当前时间,并让前端页面展示
'template/get_time.html'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>dfdfdfdfdfd</h2>
</body>
</html>
'views.py'
def get_time(request):
import time
with open('get_time.html','r') as f:
data=f.read().replace('dfdfdfdfdfd',time.strftime('%Y-%m-%d %X'))
return data
'urls.py'
urls=(('/get_time',get_time),('/error',error))
'start.py'
def run(request,response):
"""
:param request: 请求数据
:param response: 响应数据
:return: 返回给浏览器的数据
"""
print(request)
response('200 OK', []) # 固定编写
func_name=None
for url in urls:
if request.get('PATH_INFO')==url[0]:
func_name=url[1]
if func_name:
return [func_name(request).encode('utf8')]
return [error(request).encode('utf8')]
if __name__ == '__main__':
server=make_server('127.0.0.1',8080,run) # 任何访问127.0.0.1:8080的请求都会发给第三个参数加括号调用
server.serve_forever() # 永久启动
将字典数据传递给html页面,并且在页面上操作字段数据
这个无法实现:在html页面上使用类似于后端的语法操作数据
jinja2模版
jinja2能够让我们在html文件内使用类似于后端的语法来操作各种数据类型
下载jinjia2模块
pip3.8 install jinja2 # 根据pip版本下载,因为我用的是3.8的
用jinja2实现我们之前无法将字典数据传入前端并操作问题
from jinja2 import Template
# 连接数据库,将数据库的user_indo表 显示到前端页面
def get_dict(request):
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='123456',
database='db01',
charset='utf8',
autocommit=True
)
cursor=conn.cursor(cursor=pymysql.cursors.DictCursor) # 将结果集的每行数据以字典形式展示
sql='select * from user_info'
cursor.execute(sql)
user_dict=cursor.fetchall()
with open('get_dict.html', 'r', encoding='utf8') as f:
data = f.read()
temp = Template(data) # 通过源码可知,Template是一个class,加括号实例化出对象
return temp.render(user_dict=user_dict) # 通过对象点绑定给类的方法render,括号内=前面是别名,后面是传入的实参
模版语法
字典的三种取值方式
<h1>{{ data }}</h1>
<h1>{{ data['name'] }}</h1>
<h1>{{ data.get('pwd') }}</h1>
<h1>{{ data.hobby }}</h1>
jinja2的for循环语法
{% for user in user_data %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.age }}</td>
</tr>
{% endfor %}
python主流web框架
django框架
大而全,自身携带的功能非常多,类似于航空母舰
缺陷:开发小项目的时候使用该框架有点笨重(大材小用)
flask框架
小而精,自身携带的功能非常的少,类似于特种兵,主要依赖于第三方模块
缺陷:受限于第三方模块的开发
tornado框架
异步非阻塞,该模块快到可以作为游戏服务器
缺陷:上手难度是三者最高的
其他框架
fastapi框架、sanic框架等
总结
框架虽然多,但是内部逻辑大差不差,我们重点学习一个即可:django
django框架简介
版本问题
1.X:默认不支持异步
2.X:默认不支持异步
3.X:自带异步功能
框架下载
pip3.8 install django==2.2.22 # 我用的是pip3.8版本,以下都按3.8演示
启动注意事项
- 计算机名称尽量不要有中文
- 项目中所有的py文件尽量不要用中文
- 不同版本的解释器配合不同版本的django会有一些报错
- 一个pycharm窗口只运行有一个项目,不要做项目的嵌套
验证django是否下载成功
在终端输入
django-admin
显示如下代码即为下载成功
django基本操作命令
命令行操作
-
创建django项目
django-admin startproject 项目名
-
启动django项目
# 先进入项目目录下 cd 项目名 # 执行启动目录 python3.8 manage.py runserver [ip:port] # ip和端口可以不填,默认为:回环地址:8000
-
访问django服务端
-
创建app应用
python3.8 manage.py startapp 应用名
补充
django框架类似于一个空壳子,给你提供所需的资源,至于到底需要写那些功能,需要创建app划分
pycharim操作
一路点,创建app01
命令行与pycharm操作的区别
-
命令行不会自动创建templates文件夹
-
命令行不会再配置文件编写templates文件夹的配置
'DIRS': [os.path.join(BASE_DIR,'templates')]
-
pycharm自动创建的第一个应用会自动注册到配置文件中
-
针对
db.sqlite3
文件不用去在乎它有没有创建,只要运行了django会自动出来
django目录结构
目录结构
├── app01 # 应用名文件夹(可以有多个)
│ ├── __init__.py # 很少用 主要做一些冷门配置
│ ├── admin.py # django自带的后台管理
│ ├── apps.py # 创建应用之后用于应用的注册
│ ├── migrations # orm相关(数据库打交道的记录)
│ │ └── __init__.py # 很少用,主要做一些冷门配置
│ ├── models.py # 存储与数据库表相关的类
│ ├── tests.py # 自带的测试文件
│ └── views.py # 存储业务相关的逻辑代码(函数、类)
├── manage.py # django的入口文件
├── mysite # 项目同名文件夹
│ ├── __init__.py # 很少用 主要做一些冷门配置
│ ├── settings.py # 项目配置文件
│ ├── urls.py # 对应关系(目前简单的理解:网址后缀跟函数名)
│ └── wsgi.py # django服务,基本不用
└── templates # 存储项目所需的html文件
├── db.sqlite3 # 自带的小型数据库
核心目录结构
urls.py 路由层
views.py 视图层
temlates.py 模版层
models.py 模型层
django小白必会三板斧
# 启动django项目之后,如何添加更多功能
回想自己编写的web框架,如果想要添加功能,就去urls.py和views.py
# django自带重启功能
当识别到项目中代码有变化之后,隔断时间会自动重启,但是有时候较慢
# 与浏览器打交道的视图函数都应该有返回值,常见的就是下列三个
1.HttpResponse
主要用于返回字符串类型的数据
# 注意 当用HttpResponse的时候,urls路径要添加url和views对应的关系
并且在views视图中添加对应函数,返回值为HttpResponse
def index(request):
return HttpResponse('<h1>from index<h1>')
2.render
主要用于返回html页面,并且支持模版语法(django自己写的)
def login(request):
return render(request,'a.html',{'name':'jason','pwd':'123'})
3.redirect
主要用于重定向,括号内可以写其他网站的全程,也可以自己网站的后缀
def register(request):
return redirect('https://www.baidu.com')
使用django展示MySQL数据到前端页面
# html页面
<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 table-bordered">
<thead>
<tr>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
</tr>
</thead>
<tbody >
{% for user in user_dict %}
<tr class="active">
<td>{{ user.id }}</td>
<td>{{ user.username }}</td>
<td>{{ user.age }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
# urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('home/',views.home)
]
import pymysql
def home(request):
conn=pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='123456',
database='db01',
charset='utf8',
autocommit=True
)
cursor=conn.cursor(pymysql.cursors.DictCursor)
sql='select * from user_info'
cursor.execute(sql)
res=cursor.fetchall()
return render(request,'home.html',{'user_dict':res})