纯手撸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()  # 永久启动

image-20220830180717667

将字典数据传递给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演示

启动注意事项

  1. 计算机名称尽量不要有中文
  2. 项目中所有的py文件尽量不要用中文
  3. 不同版本的解释器配合不同版本的django会有一些报错
  4. 一个pycharm窗口只运行有一个项目,不要做项目的嵌套

验证django是否下载成功

在终端输入

django-admin

显示如下代码即为下载成功

image-20220830190917584

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
image

命令行与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})
posted @ 2022-08-30 19:48  荀飞  阅读(168)  评论(0编辑  收藏  举报