web框架之Django
Posted 总有问题刁难朕
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了web框架之Django相关的知识,希望对你有一定的参考价值。
web开发介绍
互联网刚兴起的那段时间大家都热衷于CS架构,也就是Client/Server模式。每个用户的电脑上安装一个Client,就像QQ这种终端软件。
随着互联网的发展,开发客户端显得越来越‘重’,BS架构(Browser/Server模式)越来越流行,也就是用户只需要一个浏览器就足够了。
前端性能优化的方式
- 减少DOM操作
- 部署前,图片压缩,代码压缩
- 优化JS代码结构,减少冗余代码
- 减少HTTP请求,合理设置 HTTP缓存
- 使用内容分发CDN加速
- 静态资源缓存
- 图片延迟加载
一个页面从输入URL到页面加载显示完成,这个过程中都发生了什么?
输入地址之后:
- 浏览器查找域名的IP地址
- 这一步包括DNS具体的查找过程,包括:浏览器缓存->系统缓存->路由器缓存…
- 浏览器向Web服务器发送一个HTTP请求
- 服务器的永久重定向响应(从 http://example.com 到 http://www.example.com)
- 浏览器跟踪重定向地址
- 服务器处理请求
- 服务器返回一个HTTP响应
- 浏览器显示html
- 浏览器发送请求获取嵌入在HTML 中的资源(如图片、音频、视频、CSS、JS等等)
- 浏览器发送异步请求
Web框架
我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。
这样我们就可以自己实现Web框架了。
1 import socket 2 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 3 sock.bind((‘127.0.0.1‘, 8000)) 4 sock.listen(5) 5 while True: 6 conn, addr = sock.accept() 7 data = conn.recv(8096) 8 conn.send(b"OK") 9 conn.close()
可以说Web服务本质上都是在这十几行代码基础上扩展出来的。这段代码就是它们的祖宗。
用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定?
你这个网站是这个规定,他那个网站按照他那个规定,这互联网还能玩么?
所以,必须有一个统一的规则,让大家发送消息、接收消息的时候有个格式依据,不能随便写。
这个规则就是HTTP协议,以后你发送请求信息也好,回复响应信息也罢,都要按照这个规则来。
那HTTP协议是怎么规定消息格式的呢?
让我们首先看下我们在服务端接收到的消息是什么。
然后再看下我们浏览器收到的响应信息是什么。
响应头在浏览器的network窗口可以看到,我们看到的HTML页面内容就是响应体。本质上还是字符串,因为浏览器认识HTML,所以才会渲染出页面。
每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。
HTTP响应的Header中有一个Content-Type
表明响应的内容格式。如text/html
表示HTML网页。
1 // 报头信息 2 HTTP/1.1 200 OK\r\n 3 Header1:v1\r\n 4 Header2:v2\r\n 5 \r\n\r\n ----------> 与内容之间隔着两个换行符 6 // --------------------------------- 内容 7 Body...
1 import socket 2 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 3 sock.bind((‘127.0.0.1‘, 8000)) 4 sock.listen(5) 5 while True: 6 conn, addr = sock.accept() 7 data = conn.recv(8096) 8 conn.send(b"HTTP/1.1 200 OK\r\n\r\n") 9 conn.send(b"OK") 10 conn.close()
这样就结束了吗?
如何让我们的Web服务根据用户请求的URL不同而返回不同的内容呢?
小事一桩,我们可以从请求头里面拿到请求的URL,然后做一个判断。
1 import socket 2 sk = socket.socket() 3 sk.bind(("127.0.0.1", 8080)) 4 sk.listen(5) 5 while True: 6 conn, addr = sk.accept() 7 data = conn.recv(8096) 8 data_str = str(data, encoding="utf-8") # 把收到的字节数据转换成字符串类型 9 header = data_str.split("\r\n\r\n") # 将请求的数据按照\r\n\r\n分割 10 header_list = header[0].split("\r\n") # 按\r\n分割header 11 temp = header_list[0].split(" ") # 按空格分割第一行 12 url = temp[1] # 取到请求的URL 13 conn.send(b"HTTP/1.1 200 OK\r\n\r\n") # 发送响应头 14 if url == "/index/": 15 conn.send(b"this is index page.") 16 else: 17 conn.send(b"404 not found.") 18 conn.close()
解决了不同URL返回不同内容的需求。
但是问题又来了,如果有很多很多页面怎么办?难道要挨个判断?
当然不用,我们有更聪明的办法。
1 import socket 2 def index(): 3 return b"this is index page." 4 def login(): 5 return b"this is login page." 6 url_func_map = [ 7 ("/index/", index), 8 ("/login/", login) 9 ] 10 sk = socket.socket() 11 sk.bind(("127.0.0.1", 8080)) 12 sk.listen(5) 13 while True: 14 conn, addr = sk.accept() 15 data = conn.recv(8096) 16 data_str = str(data, encoding="utf-8") # 把收到的字节数据转换成字符串类型 17 header = data_str.split("\r\n\r\n") # 将请求的数据按照\r\n\r\n分割 18 header_list = header[0].split("\r\n") # 按\r\n分割header 19 temp = header_list[0].split(" ") # 按空格分割第一行 20 url = temp[1] # 取到请求的URL 21 conn.send(b"HTTP/1.1 200 OK\r\ncontent-type:text/html; charset=utf-8\r\n\r\n") # 发送响应头 22 func_name = None # 将要执行的函数 23 for i in url_func_map: 24 if i[0] == url: 25 func_name = i[1] 26 break # 跳出循环 27 if func_name: # 如果找到了要执行的函数 28 response = func_name() 29 else: 30 response = b"404 not found." 31 conn.send(response) 32 conn.close()
完美解决了不同URL返回不同内容的问题。
但是我不想仅仅返回几个字符串,我想给浏览器返回完整的HTML内容,这又该怎么办呢?
没问题,不管是什么内容,最后都是转换成字节数据发送出去的。
我可以打开HTML文件,读取出它内部的二进制数据,然后发送给浏览器。
1 import socket 2 def index(): 3 with open("index.html", "rb") as f: 4 html_data = f.read() 5 return html_data 6 def login(): 7 with open("login.html", "rb") as f: 8 html_data = f.read() 9 return html_data 10 url_func_map = [ 11 ("/index/", index), 12 ("/login/", login) 13 ] 14 sk = socket.socket() 15 sk.bind(("127.0.0.1", 8080)) 16 sk.listen(5) 17 while True: 18 conn, addr = sk.accept() 19 data = conn.recv(8096) 20 data_str = str(data, encoding="utf-8") # 把收到的字节数据转换成字符串类型 21 header = data_str.split("\r\n\r\n") # 将请求的数据按照\r\n\r\n分割 22 header_list = header[0].split("\r\n") # 按\r\n分割header 23 temp = header_list[0].split(" ") # 按空格分割第一行 24 url = temp[1] # 取到请求的URL 25 conn.send(b"HTTP/1.1 200 OK\r\ncontent-type:text/html; charset=utf-8\r\n\r\n") # 发送响应头 26 func_name = None # 将要执行的函数 27 for i in url_func_map: 28 if i[0] == url: 29 func_name = i[1] 30 break # 跳出循环 31 if func_name: # 如果找到了要执行的函数 32 response = func_name() 33 else: 34 response = b"404 not found." 35 conn.send(response) 36 conn.close()
这网页能够显示出来了,但是都是静态的啊。页面的内容都不会变化的,我想要的是动态网站。
没问题,我也有办法解决。我选择使用字符串替换来实现这个需求。
1 import socket 2 def index(): 3 with open("index.html", "rb") as f: 4 html_data = f.read() 5 return html_data 6 def login(): 7 with open("login.html", "rb") as f: 8 html_data = f.read() 9 import time 10 time_str = str(time.time()) 11 html_str = str(html_data, encoding="utf-8") 12 new_html = html_str.replace("@@[email protected]@", time_str) 13 return bytes(new_html, encoding="utf-8") 14 url_func_map = [ 15 ("/index/", index), 16 ("/login/", login) 17 ] 18 sk = socket.socket() 19 sk.bind(("127.0.0.1", 8080)) 20 sk.listen(5) 21 while True: 22 conn, addr = sk.accept() 23 data = conn.recv(8096) 24 data_str = str(data, encoding="utf-8") # 把收到的字节数据转换成字符串类型 25 header = data_str.split("\r\n\r\n") # 将请求的数据按照\r\n\r\n分割 26 header_list = header[0].split("\r\n") # 按\r\n分割header 27 temp = header_list[0].split(" ") # 按空格分割第一行 28 url = temp[1] # 取到请求的URL 29 conn.send(b"HTTP/1.1 200 OK\r\ncontent-type:text/html; charset=utf-8\r\n\r\n") # 发送响应头 30 func_name = None # 将要执行的函数 31 for i in url_func_map: 32 if i[0] == url: 33 func_name = i[1] 34 break # 跳出循环 35 if func_name: # 如果找到了要执行的函数 36 response = func_name() 37 else: 38 response = b"404 not found." 39 conn.send(response) 40 conn.close()
这是一个简单的动态,我完全可以从数据库中查询数据,然后去替换我html中的对应内容,然后再发送给浏览器完成渲染。
这个过程就相当于HTML模板渲染数据。
本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据。
我这里用的特殊符号是我定义的,其实模板渲染有个现成的工具:jinja2
1 from jinja2 import Template 2 template = Template(data) # 生成模板实例 3 template.render(data=data) # 渲染数据
1 conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8") 2 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) 3 cursor.execute("select name, age, department_id from userinfo") 4 user_list = cursor.fetchall() 5 cursor.close() 6 conn.close()
创建一个测试的user表:
1 CREATE TABLE user( 2 id int auto_increment PRIMARY KEY, 3 name CHAR(10) NOT NULL, 4 habit CHAR(20) NOT NULL 5 )engine=innodb DEFAULT charset=UTF8; 6
上述代码模拟了一个web服务的本质.
而对于真实开发中的Python Web程序来说,一般分为两部分:服务器程序和应用程序。
服务器程序负责对Socket
服务器进行封装,并在请求到来时,对请求的各种数据进行整理。
应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py等。
不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。
这样,服务器程序就需要为不同的框架提供不同的支持。
这样混乱的局面无论对于服务器还是框架,都是不好的。
对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。
这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么它们就可以配合使用。
一旦标准确定,双方根据约定的标准各自进行开发即可。
WSGI(Web Server Gateway Interface)就是这样一种规范,它定义了使用Python编写的Web APP与Web Server之间接口格式,实现Web APP与Web Server间的解耦。
Python标准库提供的独立WSGI服务器称为wsgiref
。
1 from wsgiref.simple_server import make_server 2 def run_server(environ, start_response): 3 start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html‘)]) 4 return [bytes(‘<h1>Hello, web!</h1>‘, encoding=‘utf-8‘), ] 5 if __name__ == ‘__main__‘: 6 httpd = make_server(‘‘, 8000, run_server) 7 print("Serving HTTP on port 8000...") 8 httpd.serve_forever() 9
wsgiref
模块作为Web Server。Django
安装与基本配置
1 pip3 install django
1 // 命令行创建项目 2 django-admin startproject mysite 3 4 // 也可以用pycharm创建 5
1 mysite/ 2 ├── manage.py # 管理文件 3 └── mysite # 项目目录 4 ├── __init__.py 5 ├── settings.py # 配置 6 ├── urls.py # 路由 --> URL和函数的对应关系 7 └── wsgi.py # webserver --> wsgiref模块 8
1 // 命令行启动方式 2 python manage.py runserver 127.0.0.1:8000
模板文件配置:
1 # setting配置文件 2 TEMPLATES = [ 3 { 4 ‘BACKEND‘: ‘django.template.backends.django.DjangoTemplates‘, 5 ‘DIRS‘: [os.path.join(BASE_DIR, "template")], # template文件夹位置 6 ‘APP_DIRS‘: True, 7 ‘OPTIONS‘: { 8 ‘context_processors‘: [ 9 ‘django.template.context_processors.debug‘, 10 ‘django.template.context_processors.request‘, 11 ‘django.contrib.auth.context_processors.auth‘, 12 ‘django.contrib.messages.context_processors.messages‘, 13 ], 14 }, 15 }, 16 ] 17 18 # 也就是说以后存放的模板文件(html)存放位置, 可以改名字,但是相应的名字也要修改
1 # 静态文件存放位置 2 STATIC_URL = ‘/static/‘ # HTML中使用的静态文件夹前缀 3 STATICFILES_DIRS = ( 4 os.path.join(BASE_DIR, "static"), # 静态文件存放位置 5 ) 6 # 也就是以后 css,js 存放文件的位置
新手必备三件套:
1 2 from django.shortcuts import HttpResponse, render, redirect 3
以上是关于web框架之Django的主要内容,如果未能解决你的问题,请参考以下文章