今日内容概要
- 纯手撸web框架
- 基于wsgiref模块
- 框架代码优化
- 动静态网页
- jinja2模板语法
- python主流web框架
- django框架简介
- django基本操作命令
- django小白必会三板斧
纯手撸web框架
1.web框架概念
web框架可以理解为是基于互联网的web服务端(socket服务端)
2.步骤
- socket服务端代码
- HTTP协议
- 根据网址后缀的不同请求不同的内容
- 请求方式
(1)GET:朝服务端索要数据
(2)POST:朝服务端提交数据- 从请求数据格式中筛选出用户输入的网址后缀
target_url = data.decode('utf8').split(' ')[1]
- 代码的缺陷:
(1)socket代码重复编写造轮子
(2)针对请求数据格式的处理复杂且重复
(3)针对不同网址后缀的匹配方式过于简单
3.未优化代码展示
import socket server = socket.socket() # TCP协议 server.bind(('127.0.0.1', 8080)) # IP协议 PORT协议 server.listen(5) # 连接池 while True: sock, address = server.accept() data = sock.recv(1024) sock.send(b'HTTP/1.1 200 OK\r\n\r\n') # print(data.decode('utf8')) # 从字符串中截取出需要的内容 target_url = data.decode('utf8').split(' ')[1] # / /index /login /reg /xxx if target_url == '/index': sock.send(b'index view') # with open(r'my html.html','rb') as f: # sock.send(f.read()) elif target_url == '/login': sock.send(b'login view') elif target_url == '/reg': sock.send(b'reg view') else: sock.send(b'404 error')
基于wsgiref模块
wsgiref内置模块:
wsgiref内部封装了socket代码和对请求数据的处理。
from wsgiref.simple_server import make_server def run(request, response): """ :param request: 请求数据 :param response: 响应数据 :return: 返回给客户端的数据 """ print(request) # 自动将请求数据全部处理成字典k:v键值对形式 response('200 OK', []) # 固定代码 无需掌握 return [b'hello big baby'] if __name__ == '__main__': server = make_server('127.0.0.1', 8080, run) # 任何访问127.0.0.1:8080的请求都会给第三个参数加括号调用 server.serve_forever() # 永久启动
- wsgiref模块解决了两个问题:
(1)socket代码重复编写造轮子
(2)针对请求数据格式的处理复杂且重复- 思考如何再次实现根据不同的网址后缀返回不同的内容(函数化)
先从大字典中查找出记录网址后缀的键值对。
(1)不推荐使用连续的多个if判断
(2)针对面条版的代码首先应该考虑封装成函数def index(request): return 'index' def login(request): return 'login' def register(request): return 'register' def error(request): return '404 error' urls = ( ('/index', index), ('/login', login), ('/register', register), )
- 根据py文件中功能的不同划分到不同的py文件(模块化)
py文件 功能 urls.py 对应关系 views.py 功能函数 start.py 启动文件 templates文件夹 存储html
封装之后的最终代码
启动文件 (start.py ): from wsgiref.simple_server import make_server from urls import urls from views import * def run(request, response): response('200 OK', []) target_path = request.get('PATH_INFO') # /login func_name = None for url_tuple in urls: # ('/index',index) ('/login',login) if url_tuple[0] == target_path: func_name = url_tuple[1] # 先存储匹配到的函数名 break # 一旦匹配到了内容立刻结束for循环 # for循环结束之后还需要判断func_name是不是为None(有可能存在没有匹配上) if func_name: res = func_name(request) else: res = error(request) return [res.encode('utf8')] # 作统一编码处理 if __name__ == '__main__': server = make_server('127.0.0.1', 8080, run) # 任何访问>127.0.0.1:8080的请求都会给第三个参数加括号调用 server.serve_forever() # 永久启动
功能函数(views.py ) def index(request): return 'index' def login(request): return 'login' def register(request): return 'register' def error(request): return 'error'
对应关系(urls.py) `from views import * urls = ( ('/index',index), ('/login',login), ('/register',register), ) ``
动静态页面
1.动态页面
页面上的数据不是全部写死的,有些是动态获取(后端传入)
2.静态网页
页面上的数据直接写死的,要想改变只能修改源码。
3.实际需求
后端代码获取当前时间,然后让前端页面展示:
(1)字符串替换def get_time(request): import time ctime = time.strftime('%Y-%m-%d %X') with open(r'html_get_time.html','r',encoding='utf8')as f: data = f.read() # 如何将时间嵌入html内部,之后再返回给前端浏览器(字符串的替换) data = data.replace('lalala',ctime) return data
(2)将字典数据传递给html页面并且想要在页面上操作字典数据;
def get_dict(request): user_dict = {'name':'jason','pwd':123,'hobby':['read','run','music']} with open(r'html_get_dict.html','r',encoding='utf8')as f: data = f.read() # 如何将时间嵌入html内部,之后再返回给前端浏览器(字符串的替换) data = data.replace('hahaha',str(user_dict)) return data
考虑到问题:我们无法自己实现在html页面上使用类似于后端的语法操作数据。所以引出jinja2模块。
jinja2模块
1.jinjia2说明
jinja2能够让我们在html文件内使用类似于后端的语法来操作各种数据类型。
2.jinja2下载pip3 install jinja2
3.举个栗子——字典实现页面展示
views.py:
from jinja2 import Template def get_dict(request): user_dict = {'name': 'jason', 'pwd': 123, 'hobby': ['read', 'run', 'music']} with open(r'templates/myhtml04.html','r',encoding='utf8') as f: data = f.read() temp = Template(data) res = temp.render(data=user_dict) # 将字典传递给html页面 页面上通过>data即可获取(data仅仅是一个变量名) return res
start.py
from wsgiref.simple_server import make_server from urls import urls from views import * def run(request, response): response('200 OK', []) target_path = request.get('PATH_INFO') # /login func_name = None for url_tuple in urls: # ('/index',index) ('/login',login) if url_tuple[0] == target_path: func_name = url_tuple[1] # 先存储匹配到的函数名 break # 一旦匹配到了内容立刻结束for循环 # for循环结束之后还需要判断func_name是不是为None(有可能存在没有匹配上) if func_name: res = func_name(request) else: res = error(request) return [res.encode('utf8')] # 作统一编码处理 if __name__ == '__main__': server = make_server('127.0.0.1', 8080, run) # 任何访问127.0.0.1:8080的请求都会给第三个参数加括号调用 server.serve_forever() # 永久启动
urls.py
from views import * urls = ( ('/get_dict',get_dict), )
html_get_dict_html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"> <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"</script> </head> <body> <h1>{{ data }}</h1> <h1>{{ data['name'] }}</h1> <h1>{{ data.get('pwd') }}</h1> <h1>{{ data.hobby }}</h1> </body> </html>
模板语法
1.普通模板语法
<h1>{{ data }}</h1> <h1>{{ data['name'] }}</h1> <h1>{{ data.get('pwd') }}</h1> <h1>{{ data.hobby }}</h1>
2.for循环模板语法
{% for user in user_data %} <tr> <td>{{ user.id }}</td> <td>{{ user.name }}</td> <td>{{ user.age }}</td> </tr> {% endfor %}
举个栗子:前端,后端,数据库三者数据联动
- views.py文件
import pymysql def get_mysql(request): conn =pymysql.connect( host='127.0.0.1', port=3306, user='root', password='000000', database='day55', charset='utf8', autocommit=True # 做增删改操作时自动提交 ) cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) sql = 'select * from userinfo' cursor.execute(sql) user_data = cursor.fetchall() with open(r'html_get_mysql.html','r',encoding='utf8')as f: data = f.read() temp = Template(data) res = temp.render(user_data=user_data) return res ==================补充知识点之pymysql连接数据库:================== #创建数据库及表,然后插入数据 mysql> create database dbforpymysql; mysql> create table userinfo(id int not null auto_increment primary key,username varchar(10),passwd varchar(10))engine=innodb default charset=utf8; mysql> insert into userinfo(username,passwd) values('frank','123'),('rose','321'),('jeff',666); #查看表内容 mysql> select * from userinfo; +----+----------+--------+ | id | username | passwd | +----+----------+--------+ | 1 | frank | 123 | | 2 | rose | 321 | | 3 | jeff | 666 | +----+----------+--------+ 3 rows in set (0.00 sec) import pymysql #连接数据库 db = pymysql.connect("localhost","root","LBLB1212@@","dbforpymysql") #使用cursor()方法创建一个游标对象 cursor = db.cursor() #使用execute()方法执行SQL语句 cursor.execute("SELECT * FROM userinfo") #使用fetall()获取全部数据 data = cursor.fetchall() #打印获取到的数据 print(data) #关闭游标和数据库的连接 cursor.close() db.close() #运行结果 ((1, 'frank', '123'), (2, 'rose', '321'), (3, 'jeff', '666'))
- start.py文件
如上面雷同- urls.py文件
from views import * urls = ( ('/get_mysql',get_mysql), )
- html_get_mysql,html文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"> <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> <div class="container"> <div class="row"> <h1 class="text-center">数据展示</h1> <div class="col-md-6 col-md-offset-3"></div> <table class="table table-hover table-striped"> <thead> <tr> <th>主键</th> <th>姓名</th> <th>年龄</th> </tr> </thead> <tbody> {% for user in user_data %} <tr> <td>{{user.id}}</td> <td>{{user.name}}</td> <td>{{user.age}}</td> </tr> {% endfor %} </tbody> </table> </div> </div> </body> </html>
python主流web框架
1.django框架
大而全,自身携带的功能非常多,类似于航空母舰。
缺陷:开发小项目的时候使用该框架有点笨重(大材小用)
2.flask框架
小而精,自身携带的功能非常少,类似于特种兵,主要依赖于第三方模块。
缺陷:受限于第三方模块的研发
3.tornado框架
异步非阻塞,该框架快到可以作为游戏服务器;
缺陷:上手难度是三者中最高的。
此外还有fastapi框架,sanic框架等
django框架简介
1.版本问题
(1)1.X(例如版本1.11):同步
(2)2.X(例如版本2.2):同步
(3)3.X(例如版本3.2):异步
2.启动注意事项
(1)计算机名称尽量不要有中文;
(2)项目中所有的py文件名尽量不要用中文;
(3)不同版本的python解释器配合不同版本的django,会出现一些报错,这种情况下可以仔细查找一下报错信息,里面会提示你是哪个py文件里面的代码出错,找到并把逗号删除。第152行: widgets.py
(4)一个pycharm窗口只允许有一个项目,不要做项目的嵌套。
3.验证django是否下载成功
cmd终端输入django-admin
django基本操作命令
在启动django项目的时候,一定要确保一个端口只有一个项目。
1.命令行操作
- 创建django项目
django-admin startproject 项目名
- 启动django项目
(1)先切换到项目目录下
cd 项目名
(2)执行启动目录
python38 manage.py runserver ip:port
访问django服务端
浏览器直接访问创建app应用
python38 manage.py startapp 应用名 说明: django框架类似于是一个空壳子,给你提供所需的资源; 至于到底要写哪些功能,需要通过创建app来划分; eg:eg:django初始项目可以看成是一所大学,app就相当于是大学里面的各个学院。
2.pycharm操作
鼠标操作即可。
如果遇到运行报错可以将setting.py文件中这行代码中/改为,即可!!!
3.命令行与pycharm操作的区别
- 命令行不会自动创建templates文件夹
- 命令行不会在配置文件编写关于templates文件夹的配置
'DIRS': [os.path.join(BASE_DIR, 'templates')]
- pycharm自动创建的第一个应用会自动注册到配置文件中
'app01.apps.App01Config',
- 针对db.sqlite3文件不用去在乎它有没有创建,只要运行了django会自动出来。
django目录结构
1.项目同名文件夹
文件名 说明 init.py 很少用,主要做一些冷门配置 settings.py 项目配置文件 urls.py 对应关系(目前简单的理解:网址后缀跟函数名) wsgi.py django服务,基本不用 manage.py django入口文件 templates文件夹 存储项目所需的html文件
2.应用名文件夹(可以有多个)
文件名 说明 migrations文件夹 orm相关(数据库打交道的记录) init.py 很少用,主要做一些冷门配置 admin.py django自带的后台管理 apps.py 创建应用之后用于应用的注册 models.py 存储与数据库表相关的类 tests.py 自带的测试文件 views.py 存储业务相关的逻辑代码(函数、类) db.sqlite3 自带的小型数据库
3.其他
文件名 说明 urls.py 路由层 views.py 视图层 templates 模板层 models.py 模型层
django小白必会三板斧
- HttpResponse
主要用于直接返回字符串类型的数据def index(request): return HttpResponse('你好啊,我是django2.2版本')
- render
主要用于返回html页面,并且支持模板语法def func(request): return render(request, 'func.html') =======同时也支持传值============= def func(request): user_dict = {'name':'jason','pwd':123} return render(request, 'func.html',{'data':user_dict})
- redirect
主要用于页面重定向def login(request): # 针对别人的地址: return redirect('https://www.baidu.com') # 针对自己的地址: return redirect('/index/')