django 框架初步了解
wed 框架本质及第一个Django实例
web 框架本质
我们可以这样立即:所有的web 应用本质就是一个socket服务端
这样我们就可以自己实现web框架了。
半成品自定义web框架
import socket sk=socket.socket() sk.bind(("127.0.0.1",8080)) sk.listen() while True: conn,addr=sk.accept() data = conn.recv(8096)
print(data)#将浏览器发来的消息打印出来 conn.send(b"ok") conn.close()
用户的浏览器一输入网址, 会给服务端发送数据, 那浏览器会发送什么数据, 怎么发, 这个谁来定,你这个网站是这个规定, 他那个网站按照他那个规定 , 这样互联网就不能玩了,
所以,必须有一个统一的规则,让大家发送消息, 接受消息的时候 有个格式依据, 不能随便写。这个规则就是HTTP 协议, 以后浏览器发送请求信息也好, 服务器回复响应信息也罢,都要按照这个规则来。HTTP 协议主要规定了客户端和服务器之间的通信格式 那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'
然后在看一下我们平时访问博客园官网时浏览器收到的响应信息是什么。
响应相关信息可以在浏览器调试窗口network标签页中看到
点击view source 之后显示如下图:
我们发现收发的消息需要按照一定的额格式来, 这里就需要了解一下HTTP协议了,
http协议对收发消息的格式要求:
每个http 请求和响应都遵循相同的格式, 一个http包含header 和body两部分, 其中body是可以选的。
http 响应的header 中有一个conten-Type 表明响应的内容格式, 如text/html 表示HTML 网页。
http GET 请求的格式
http 响应的格式:
处女版自定义的web框架
经过上面的补充学习, 我们知道了 要想让我们自己写的web server 端正起来, 必须要让我们的WEB server 在给客户端回复消息的时候按照HTTP 协议的规则加上响应状态行, 这样我们就实现了一个正经的web 框架了。
import socket server =socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind(("127.0.0.1",8888)) server.listen() while 1: conn,addr=server.accept() data = conn.recv(8096) print(data)
#给回复的消息加上响应的状态行 conn.send(b"HTTP/1.1 200 OK\r\n\r\n<h1>o89k</h1>") conn.colse()
这个是web框架的本质。还可以继续完善。
跟距不同的路径返回不同的内容。
#根据URL 中不同的路径返回不同的内容 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") #把收到的字节类型的数据转换成字符串 data1=data.split("\r\n")[0] #按照\r\n 分割 url =data.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()
上面的代码解决了不同的url 路径返回不同内容的需求
那么问题又来了, 如果有很多很多路径要判断怎么办 , 不可能要挨个写if判断,我们有更聪明的办法:
根据不同的路径返回不同的内容——函数版
#根据url 中不同的路径返回不同的内容--函数版 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="utf-8") 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") #把收到的字节类型数据转换成字符串 data1= data.split("\r\n")[0] #按照\r\n 分割 url=data1.split()[1] #url是我们从浏览器发过来的消息中分离出的访问路径 conn.send(b"HTTO/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()
根据不同的路径返回不同的内容--函数进阶版
上面的代码还是要挨个写if判断, 我们还可以在精进
根据不同的路径返回不同的内容--函数进阶版
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") list1=[ ("/index/",index), ("/home/",home), ] while 1: conn,add=sk.acccpt() data =conn,recv(8086) data=str(data,encoding="utf8") data1=data.split("\r\n")[0] url =data1.split()[1] conn,send(b"http/1.1 200 OK\r\n\r\n") 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()
完美的解决了不同的url返回不同的内容的问题, 但是我不想仅仅返回几个字符串, 我想给浏览器返回完整的HTM 内容, 这又改怎么办呢。
这是没有问题的, 不管是什么内容,最后都是转换成字节数据发送出去的, 我们可以打开HTML 文件, 读取出它内部的二进制数据, 然后在发送给浏览器
import socket sk=socket.socket() sk.bind(("127.0.0.1",8080)) sk.listen() def index(url): 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="uft8") list1=[ ("/index/",index), ("/home/",home), ] while 1: conn,add=sk.accept() data=conn.recv(8096) data =str(data,encoding="utf8") data1=data.split("\r\n")[0] url=data1.split()[1] conn.send(b"http/1.1 200 ok\r\n\r\n") 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()
这样网页都能够显示出来了, 但是都是静态的, 页面的内容都不会变化的, 而我们想要的是动态网站。米有问题的, 都有办法解决。 我们可以选择使用字符串替换来实现这个需求(这里使用事件戳来模拟动态的数据)
# 根据URL 中不同的路径返回不同的内容-函数进阶版 # 返回HTML 页面 # 让网页动态起来 import socket import time sk = socket sk.bind(("127.0.0.1",8080)) 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.accpet() data = conn.recv(8096) data=str(data,encoding="utf8") data1=data.split("\r\n")[0] url=data1.split()[1] conn.send(b"HTTP/1.1 200 OK\R\N\R\N") 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!" cinn.send(response) conn,close()
服务器程序和应用程序
对于真实开发中的python web 程序来说, 一般会分为两部分, 服务器程序和应用程序。
服务器程序负责对socket 服务器进行封装, 并在请求到来时 ,对请求的各种数据进行整理。
应用程序则负责具体的逻辑处理,为了方便应用程序的开发, 就出现了众多的web框架, 例如:
django,Flask web.py 等 不同的额框架不同的开发方式, 但是无论如何开发出的应用程序都要和服务器程序配合, 才能为用户提供服务。
这样服务器程序就需要为不同的狂阶提供不同的支持, 这样混乱的局面无论对于服务器还是框架, 都是不好的, 对服务器来说, 需要支持各种不同的框架, 对框架来说, 只有支持他的服务器才能被开发出的应用使用。
这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。
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(ti,time.time()) s=s.replace("@@oo@@",now) return bytes(s,encoding="utf8") def home(url): with open("home.html","r",encoding="utd8")as f: s=f.read() return bytes(s,encoding="utf8") list1=[ ("/index/",index), ("/home/",home), ] def run_server(environ,start_response): start_response("200 ok",[("content-Type","text/html;charset=utf8")]) url=environment['PATH_INFO'] func=None for i in list1: if i[0]==url: func=i[0] break if func: response=func(url) else: response=b"404 not found!" return [response,] if __name__=="__main__": http=make_server("127.0.0.1",8090,run_server) print("我在8090等你哦。。。") httpd.serve_forever()
jinja2
上面的代码实现了一个简单的动态, 我们完全可以从数据库中查询数据, 然后去替换我html中对应的内容,然后在发送给浏览器完成渲染, 这个过程就相当于HTML 模板渲染数据, 本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据, 我这里用的特殊符号是我定义的, 其实模板渲染有个现成的工具JINJA2
下载jinja2:
pip install jinja2
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>姓名:{{name}}</h1> <h1>爱好</h1> <ul> {% for hobby in hobby_List %} <li>{{hobby}}</li> {% endfor %} </ul> </body> </html>
使用jinja2渲染iindex2.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", asswd="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的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。
django
1:下载: setting-->project:-->project interpreter -->+ -->输入django
创建一个django 项目:
最后一定要以一个新的窗口打开:
这样是创建一个完整的DJANGO 项目。
django-admin startproject mysite
目录介绍:
mysite/ ├── manage.py # 管理文件 └── mysite # 项目目录 ├── __init__.py ├── settings.py # 配置 ├── urls.py # 路由 --> URL和函数的对应关系 └── wsgi.py # runserver命令就使用wsgiref模块做简单的web server
运行Django 项目:
python manage.py runserver 127.0.0.1:8000
模板文件配置:
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"), # 静态文件存放位置 ]
看不明白的话可以看图:
DJANGO基础三件套:
from django.shortcuts import HttpResponse, render, redirect
httpresponse
内部传入一个字符串参数 返回给浏览器
def index(request): # 业务逻辑代码 return HttpResponse("OK")
render
除request参数外还接受一个待渲染的模板文件和一个保存具体数据的字典参数。
将数据填充进模板文件,最后把结果返回给浏览器。(类似于我们上面用到的jinja2)
例如:
def index(request): # 业务逻辑代码 return render(request, "index.html", {"name": "alex", "hobby": ["烫头", "泡吧"]})