web框架之Django

Posted 总有问题刁难朕

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了web框架之Django相关的知识,希望对你有一定的参考价值。

web开发介绍

互联网刚兴起的那段时间大家都热衷于CS架构,也就是Client/Server模式。每个用户的电脑上安装一个Client,就像QQ这种终端软件。
随着互联网的发展,开发客户端显得越来越‘重’,BS架构(Browser/Server模式)越来越流行,也就是用户只需要一个浏览器就足够了。

前端性能优化的方式
  1. 减少DOM操作
  2. 部署前,图片压缩,代码压缩
  3. 优化JS代码结构,减少冗余代码
  4. 减少HTTP请求,合理设置 HTTP缓存
  5. 使用内容分发CDN加速
  6. 静态资源缓存
  7. 图片延迟加载

一个页面从输入URL到页面加载显示完成,这个过程中都发生了什么?

输入地址之后:

  1. 浏览器查找域名的IP地址
  2. 这一步包括DNS具体的查找过程,包括:浏览器缓存->系统缓存->路由器缓存…
  3. 浏览器向Web服务器发送一个HTTP请求
  4. 服务器的永久重定向响应(从 http://example.comhttp://www.example.com)
  5. 浏览器跟踪重定向地址
  6. 服务器处理请求
  7. 服务器返回一个HTTP响应
  8. 浏览器显示html
  9. 浏览器发送请求获取嵌入在HTML 中的资源(如图片、音频、视频、CSS、JS等等)
  10. 浏览器发送异步请求

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请求和响应都遵循相同的格式,一个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...
让我们的Web框架在给客户端回复响应的时候按照HTTP协议的规则加上响应头,这样我们就实现了一个正经的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"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)  # 渲染数据
pymysql连接数据库:
  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 
Django开发模式就是使用的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的主要内容,如果未能解决你的问题,请参考以下文章

web框架之Django

Python全栈开发-web框架之django

PythonWeb框架之Django初识

django之python网站开发基础

Python之Django框架

Python之Web框架Django