python day51

今日内容


Web框架的本质

咱们能够这样理解:全部的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。 这样咱们就能够本身实现Web框架了。html

简易版本的自定义web框架代码:

import socket ​ sk = socket.socket() sk.bind(("127.0.0.1", 80)) sk.listen() ​ while True: conn, addr = sk.accept() data = conn.recv(8096) conn.send(b"OK") conn.close()

总结:

用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定?因此,有一个统一的规则,让你们发送消息、接收消息的时候有个格式依据,不能随便写。这个规则就是HTTP协议,之后浏览器发送请求信息也好,服务器回复响应信息也罢,都要按照这个规则来。前端

HTTP协议

超文本传输协议:规定了客户端与服务端消息传输的格式python

四大特性:

  • 1.基于TCP/IP协议做用于应用层的协议
  • 2.基于请求响应
  • 3.无状态
  • 4.无链接

数据格式之请求:

  • 请求首行
  • 请求头(一堆k,v键值对)
  • /r/n
  • 请求体(post请求携带的数据)

数据格式之响应:

  • 响应首行
  • 响应头(一堆k,v键值对)
  • /r/n
  • 响应体(post请求携带的数据)

响应状态码:

  • 1XX 服务器已经成功接受到你的数据正在处理,你能够继续提交其余数据
  • 2XX 请求成功,服务器已经将你请求的数据发送给你了
  • 3XX 重定向
  • 4XX 请求资源不存在
  • 5XX 服务器错误

HTTP协议主要规定了客户端和服务器之间的通讯格式,让咱们首先打印下咱们在服务端接收到的消息是什么。

# b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nDNT: 1\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv\r\n\r\n
接收的原始数据

咱们发现收发的消息须要按照必定的格式来:

''' 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; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\r\n Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n DNT: 1\r\n Accept-Encoding: gzip, deflate, br\r\n Accept-Language: zh-CN,zh;q=0.9\r\n Cookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv\r\n \r\n '''

结论:

以前那些看起来比较杂乱的数据,变成了一排排的键值对,具体名称见上.

增强版的自定义版web框架:

import socket ​ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('127.0.0.1', 8000)) sock.listen() ​ while True: conn, addr = sock.accept() data = conn.recv(8096) # 给回复的消息加上响应状态行
    conn.send(b"HTTP/1.1 200 OK\r\n\r\n") conn.send(b"OK") conn.close()

这样咱们经过十几行代码就能简单地演示web 框架的本质。mysql

根据不一样的路径返回不一样的内容

import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080))  # 绑定IP和端口
sk.listen()  # 监听
while 1: # 等待链接
    conn, add = sk.accept() data = conn.recv(8096)  # 接收客户端发来的消息
    # 从data中取到路径
    data = str(data, encoding="utf8")  # 把收到的字节类型的数据转换成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0] url = data1.split()[1]  # url是咱们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由于要遵循HTTP协议,因此回复的消息也要加状态行
    # 根据不一样的路径返回不一样内容
    if url == "/index/": response = b"index"
    elif url == "/home/": response = b"home"
    else: response = b"404 not found!" ​ conn.send(response) conn.close()

根据不一样的路径返回不一样的内容--函数版

import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080))  # 绑定IP和端口
sk.listen()  # 监听
# 将返回不一样的内容部分封装成函数
def index(url): s = "这是{}页面!".format(url) return bytes(s, encoding="utf8") ​ def home(url): s = "这是{}页面!".format(url) return bytes(s, encoding="utf8") ​ while 1: # 等待链接
    conn, add = sk.accept() data = conn.recv(8096)  # 接收客户端发来的消息
    # 从data中取到路径
    data = str(data, encoding="utf8")  # 把收到的字节类型的数据转换成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0] url = data1.split()[1]  # url是咱们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由于要遵循HTTP协议,因此回复的消息也要加状态行
    # 根据不一样的路径返回不一样内容,response是具体的响应体
    if url == "/index/": response = index(url) elif url == "/home/": response = home(url) else: response = b"404 not found!" ​ conn.send(response) conn.close()

根据不一样的路径返回不一样的内容--函数进阶版

import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080))  # 绑定IP和端口
sk.listen()  # 监听
# 将返回不一样的内容部分封装成函数
def index(url): s = "这是{}页面!".format(url) return bytes(s, encoding="utf8") ​ def home(url): s = "这是{}页面!".format(url) return bytes(s, encoding="utf8") ​ # 定义一个url和实际要执行的函数的对应关系
list1 = [ ("/index/", index), ("/home/", home), ] ​ while 1: # 等待链接
    conn, add = sk.accept() data = conn.recv(8096)  # 接收客户端发来的消息
    # 从data中取到路径
    data = str(data, encoding="utf8")  # 把收到的字节类型的数据转换成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0] url = data1.split()[1]  # url是咱们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由于要遵循HTTP协议,因此回复的消息也要加状态行
    # 根据不一样的路径返回不一样内容
    func = None  # 定义一个保存将要执行的函数名的变量
    for i in list1: if i[0] == url: func = i[1] break
    if func: response = func(url) else: response = b"404 not found!"# 返回具体的响应消息
 conn.send(response) conn.close()

返回具体的HTML文件

""" 根据URL中不一样的路径返回不一样的内容--函数进阶版 返回独立的HTML页面 """import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080))  # 绑定IP和端口
sk.listen()  # 监听
# 将返回不一样的内容部分封装成函数
def index(url): # 读取index.html页面的内容
    with open("index.html", "r", encoding="utf8") as f: s = f.read() # 返回字节数据
    return bytes(s, encoding="utf8") ​ def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") ​ # 定义一个url和实际要执行的函数的对应关系
list1 = [ ("/index/", index), ("/home/", home), ] ​ while 1: # 等待链接
    conn, add = sk.accept() data = conn.recv(8096)  # 接收客户端发来的消息
    # 从data中取到路径
    data = str(data, encoding="utf8")  # 把收到的字节类型的数据转换成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0] url = data1.split()[1]  # url是咱们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由于要遵循HTTP协议,因此回复的消息也要加状态行
    # 根据不一样的路径返回不一样内容
    func = None  # 定义一个保存将要执行的函数名的变量
    for i in list1: if i[0] == url: func = i[1] break
    if func: response = func(url) else: response = b"404 not found!"# 返回具体的响应消息
 conn.send(response) conn.close()

动静态网页

静态网页:

页面上的数据都是写死的,万年不变web

动态网页:

  • 页面上的数据是从后端动态获取的
  • 好比后端获取当前时间
  • 后端获取数据库数据而后传递给前端页面

制做成动态网页:

""" 根据URL中不一样的路径返回不一样的内容--函数进阶版 返回HTML页面 让网页动态起来 """import socket import time ​ sk = socket.socket() sk.bind(("127.0.0.1", 8080))  # 绑定IP和端口
sk.listen()  # 监听
# 将返回不一样的内容部分封装成函数
def index(url): with open("index.html", "r", encoding="utf8") as f: s = f.read() now = str(time.time()) s = s.replace("@@oo@@", now)  # 在网页中定义好特殊符号,用动态的数据去替换提早定义好的特殊符号
    return bytes(s, encoding="utf8") ​ def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") ​ # 定义一个url和实际要执行的函数的对应关系
list1 = [ ("/index/", index), ("/home/", home), ] ​ while 1: # 等待链接
    conn, add = sk.accept() data = conn.recv(8096)  # 接收客户端发来的消息
    # 从data中取到路径
    data = str(data, encoding="utf8")  # 把收到的字节类型的数据转换成字符串
    # 按\r\n分割
    data1 = data.split("\r\n")[0] url = data1.split()[1]  # url是咱们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')  # 由于要遵循HTTP协议,因此回复的消息也要加状态行
    # 根据不一样的路径返回不一样内容
    func = None  # 定义一个保存将要执行的函数名的变量
    for i in list1: if i[0] == url: func = i[1] break
    if func: response = func(url) else: response = b"404 not found!"# 返回具体的响应消息
 conn.send(response) conn.close()

服务器程序和应用程序

  • 对于真实开发中的python web程序来讲,通常会分为两部分:服务器程序和应用程序。
  • 服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各类数据进行整理。

软件开发架构:

C/S架构:客户端 服务端

B/S架构:浏览器 服务器

本质上b/s架构其实也是c/s架构sql

应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。数据库

Web框架

python三大主流web框架:

  • Django:大而全,自带了不少功能模块,相似于航空母舰 (缺点:有点笨重)
  • Flask:短小精悍,自带的功能模块特别少,大部分都是依赖于第三方模块(小而轻)
  • Tornado:异步非阻塞 主要用在处理高io 多路复用的状况 能够写游戏后端

咱们根据三样元素来统计他们的特色:

  • a:socket
  • b:路由与视图函数
  • c:模板渲染

Django:

  • a用的别人的 wsgiref
  • b本身写的
  • c本身写的

Flask:

  • a用的别人的 werkzeug
  • b本身写的
  • c用的别人的 jinja2

Tornado:

a,b,c都是本身写的django

简易版本的web请求流程图

关于使用Django框架的注意事项(重要):

  • 1.计算机的名称不能有中文
  • 2.一个pycharm窗口就是一个项目,不要多个项目放在一个窗口里面
  • 3.项目名不能起中文

Django

版本问题以及下载

咱们一般都推荐下载1.11.11版本,在下载时也有两种方式后端

命令行下载:

  • pip3 install django==1.11.11
  • django-admin(验证是否下载成功)

pycharm下载:

  • pip3 install django==1.11.11
  • django-admin(验证是否下载成功)

建立Django项目的方式:

命令行建立

  • 建立django项目:

    • django-admin startproject 项目名
  • 建立app应用:

    • python3 manage.py startapp 应用名
  • 启动django项目:

    • python3 manage.py runserver

PS:用命令行建立django默认不会自动建立templates文件夹,须要你手动本身建立(注意改文件夹路径是否被添加配置文件中,以下段代码中的DIRS:[ ]中添加)

模板文件配置:

TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, "template")],  # template文件夹位置
        'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]

静态文件配置:

STATIC_URL = '/static/'  # HTML中使用的静态文件夹前缀
STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"),  # 静态文件存放位置
]

刚开始学习时可在配置文件中暂时禁用csrf中间件,方便表单提交测试.

MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]

Pycharm建立

建立django项目:

FILE >>> new project 选择第二个django 须要注意名字不能有中文,选择本地的解释器,勾选后台管理浏览器

建立app应用:

  • 1.pycharm命令行建立 >>> python3 manage.py startapp 应用名
  • 2.Tools下面run manage task功能栏

启动django项目:

点击上面绿色小箭头!

注意:

  • 1.用django必定要保证只有一个在运行状态,切记切记!
  • 2.必定记得清浏览器的缓存

app应用的概念

一个django项目就是一所大学,app就是大学里面的学院

注意新建立的app须要在配置文件中注册才能生效!

INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config'  # 能够用全称
    'app01'                  # 也能够简写
]

Django各个文件的做用

应用名字

  • migrations 数据库迁移记录相关数据
  • admin.py Django后台管理相关
  • models.py 模型表相关
  • views.py 视图函数相关

项目名

  • settings.py 配置文件
  • urls.py 路由与视图函数的映射关系

templates

项目用到的全部的html文件

manage.py

Django入口文件


WSGI标准

不一样的框架有不一样的开发方式,可是不管如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。

服务器程序须要为不一样的框架提供不一样的支持。这样混乱的局面不管对于服务器仍是框架,都是很差的。对服务器来讲,须要支持各类不一样框架,对框架来讲,只有支持它的服务器才能被开发出的应用使用。

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

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

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

引伸出的wsgiref模块

咱们利用wsgiref模块来替换咱们本身写的web框架的socket server部分:

""" 根据URL中不一样的路径返回不一样的内容--函数进阶版 返回HTML页面 让网页动态起来 wsgiref模块版 """import time from wsgiref.simple_server import make_server ​ # 将返回不一样的内容部分封装成函数
def index(url): with open("index.html", "r", encoding="utf8") as f: s = f.read() now = str(time.time()) s = s.replace("@@oo@@", now) return bytes(s, encoding="utf8") ​ def home(url): with open("home.html", "r", encoding="utf8") as f: s = f.read() return bytes(s, encoding="utf8") ​ # 定义一个url和实际要执行的函数的对应关系
list1 = [ ("/index/", index), ("/home/", home), ] ​ def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 设置HTTP响应的状态码和头信息
    url = environ['PATH_INFO']  # 取到用户输入的url
    func = None for i in list1: if i[0] == url: func = i[1] break
    if func: response = func(url) else: response = b"404 not found!"
    return [response, ] ​ if __name__ == '__main__': httpd = make_server('127.0.0.1', 8090, run_server) print("我在8090等你哦...") httpd.serve_forever()

引伸出的jinjia2模块

上面的代码实现了一个简单的动态,我彻底能够从数据库中查询数据,而后去替换我html中的对应内容,而后再发送给浏览器完成渲染。 本质上就是HTML内容中利用一些特殊的符号来替换要展现的数据。 我这里用的特殊符号是我定义的,其实模板渲染有个现成的工具: "jinja2"

上述过程就至关于HTML模板渲染数据。

关于HTML页面渲染

后端生成的数据直接传递给前端页面使用(而且前端页面能够灵活的操做改数据) >>> 模板语法

PS:模板语法,jinja2支持前端直接使用相似于python的语法操做数据

下载jinja2:

# 终端输入便可,也能够用命令行输入
pip install jinja2

index.html文件:

<!--渲染前-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Title</title>
</head>
<body>
    <h1>姓名:{{name}}</h1>
    <h1>爱好:</h1>
    <ul> {% for hobby in hobby_list %} <li>{{hobby}}</li> {% endfor %} </ul>
</body>
</html>

渲染index.html文件示例:

from wsgiref.simple_server import make_server from jinja2 import Template ​ def index(): with open("index2.html", "r") as f: data = f.read() template = Template(data)  # 生成模板文件
    ret = template.render({"name": "Alex", "hobby_list": ["烫头", "泡吧"]})  # 把数据填充到模板里面
    return [bytes(ret, encoding="utf8"), ] ​ def home(): with open("home.html", "rb") as f: data = f.read() return [data, ] ​ # 定义一个url和函数的对应关系
URL_LIST = [ ("/index/", index), ("/home/", home), ] ​ def run_server(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 设置HTTP响应的状态码和头信息
    url = environ['PATH_INFO']  # 取到用户输入的url
    func = None  # 将要执行的函数
    for i in URL_LIST: if i[0] == url: func = i[1]  # 去以前定义好的url列表里找url应该执行的函数
            break
    if func:  # 若是能找到要执行的函数
        return func()  # 返回函数的执行结果
    else: return [bytes("404没有该页面", encoding="utf8"), ] ​ if __name__ == '__main__': httpd = make_server('', 8000, run_server) print("Serving HTTP on port 8000...") httpd.serve_forever()

如今的数据是咱们本身手写的,咱们也能够从数据库中获取查询获取数据填充页面

使用pymysql链接数据库:

conn = pymysql.connect( host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8" ) cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select name, age, department_id from userinfo") user_list = cursor.fetchall() cursor.close() conn.close()

建立一个测试的user表:

CREATE TABLE user( id int auto_increment PRIMARY KEY, name CHAR(10) NOT NULL, hobby CHAR(20) NOT NULL )engine=innodb DEFAULT charset=UTF8;

模板的原理就是字符串替换,咱们只要在HTML页面中遵循jinja2的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。

模板替换语法(字典部分)在前端页面部分的代码:

<p>{{ user_dic }}</p>
<p>{{ user_dic.name }}</p>
<p>{{ user_dic['password'] }}</p>
<p>{{ user_dic.get('name') }}</p> {% for user in user_dict %}  <!--[{},{},{},{}]-->
        <tr>
            <td>{{ user.id }}</td>
            <td>{{ user.name }}</td>
            <td>{{ user.password }}</td>
        </tr> {% endfor %}

Django基础必备三件套:

from django.shortcuts import HttpResponse, render, redirect

HttpResponse

内部传入一个字符串参数,返回给浏览器。

示例:

def index(request): # 业务逻辑代码
    return HttpResponse("OK")

render

除request参数外还接受一个待渲染的模板文件和一个保存具体数据的字典参数。将数据填充进模板文件,最后把结果返回给浏览器。(相似于咱们上面用到的jinja2)

示例(两种方式):

#第一种
def reg(request): user_dict = {'name':'jason','password':'123'} return render(request,'reg.html',{'user_dict':user_dict} ​ #第二种
def reg(request): user_dict = {'name':'jason','password':'123'} return render(request,'reg.html',locals())      

redirect

接受一个URL参数,表示跳转到指定的URL。

示例:

def index(request):
    # 业务逻辑代码
    return redirect("/home/")

关于重定向

 

相关文章
相关标签/搜索