Web框架本质
我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。 这样我们就可以自己实现Web框架了。
总的来说:Web框架的本质就是浏览器和服务器基于socket套接字实现请求和响应的过程
半成品自定义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()
可以说Web服务本质上客户端和服务端基于socket进行的请求和响应的过程。这段代码就是它们的祖宗。
用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定? 你这个网站是这个规定,他那个网站按照他那个规定,这互联网还能玩么?
所以,必须有一个统一的规则,让大家发送消息、接收消息的时候有个格式依据,不能随便写。
这个规则就是HTTP协议,以后浏览器发送请求信息也好,服务器回复响应信息也罢,都要按照这个规则来。
HTTP协议主要规定了客户端和服务器之间的通信格式,那HTTP协议是怎么规定消息格式的呢?
让我们首先打印下我们在服务端接收到的消息是什么。
import socket sk = socket.socket() sk.bind(("127.0.0.1", 80)) sk.listen() while True: conn, addr = sk.accept() data = conn.recv(8096) print(data) # 将浏览器发来的消息打印出来 conn.send(b"OK") conn.close()
输出:
b'GET / HTTP/1.1 Host: 127.0.0.1:8080 Connection: keep-alive Upgrade-Insecure-Requests: 1 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 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 DNT: 1 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv '
然后我们再看一下我们访问博客园官网时浏览器收到的响应信息是什么。
响应相关信息可以在浏览器调试窗口的network标签页中看到。
我们发现收发的消息需要按照一定的格式来,这里就需要了解一下HTTP协议了。
HTTP协议对收发消息的格式要求
每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。 HTTP响应的Header中有一个 Content-Type
表明响应的内容格式。如 text/html
表示HTML网页。
HTTP GET请求的格式:
GET /path HTTP/1.1 header1:v1 header2:v2
使用
分隔多个header
HTTP POST请求格式:
POST /path HTTP/1.1 header1:v1 header2:v2 请求体...
当遇到连续两个
时,表示Header部分结束了,后面的数据是Body。
HTTP响应的格式:
200 OK Header1:v1 Header2:v2 响应体...
处女版自定义web框架
经过上面的补充学习,我们知道了要想让我们自己写的web server端正经起来,必须要让我们的Web server在给客户端回复消息的时候按照HTTP协议的规则加上响应状态行,这样我们就实现了一个正经的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 ") conn.send(b"OK") conn.close()
我们通过十几行代码简单地演示了web 框架的本质。
接下来就让我们继续完善我们的自定义web框架吧!
根据不同的路径返回不同的内容
这样就结束了吗? 如何让我们的Web服务根据用户请求的URL不同而返回不同的内容呢?
小事一桩,我们可以从请求相关数据里面拿到请求URL的路径,然后拿路径做一个判断...
""" 根据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(" ")[0] url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径 conn.send(b'HTTP/1.1 200 OK ') # 因为要遵循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="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") # 把收到的字节类型的数据转换成字符串 # 按 分割 data1 = data.split(" ")[0] url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径 conn.send(b'HTTP/1.1 200 OK ') # 因为要遵循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判断,怎么办?我们还是有办法!(只要思想不滑坡,方法总比问题多!)
""" 根据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="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") # 把收到的字节类型的数据转换成字符串 # 按 分割 data1 = data.split(" ")[0] url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径 conn.send(b'HTTP/1.1 200 OK ') # 因为要遵循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内容,这又该怎么办呢?
没问题,不管是什么内容,最后都是转换成字节数据发送出去的。 我们可以打开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") # 把收到的字节类型的数据转换成字符串 # 按 分割 data1 = data.split(" ")[0] url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径 conn.send(b'HTTP/1.1 200 OK ') # 因为要遵循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()
让网页动态起来
这网页能够显示出来了,但是都是静态的啊。页面的内容都不会变化的,我想要的是动态网站。
没问题,我也有办法解决。我选择使用字符串替换来实现这个需求。(这里使用时间戳来模拟动态的数据)
""" 根据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") # 把收到的字节类型的数据转换成字符串 # 按 分割 data1 = data.split(" ")[0] url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径 conn.send(b'HTTP/1.1 200 OK ') # 因为要遵循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()
浏览器和服务器实现请求和响应的顺序
浏览器(socket客户端) 2. www.cnblogs.com(42.121.252.58,80) sk.socket() sk.connect((42.121.252.58,80)) sk.send('我想要xx') 5. 接收 6. 连接断开 博客园(socket服务端) 1. 监听ip和端口(42.121.252.58,80) while True: 用户 = 等待用户连接 3. 收到'我想要xx' 4. 响应:“好” 用户断开
在客户端和服务端进行完一次请求和响应后会自动断开,当再次请求和响应的时候会重新来过,所以HTTP协议是无状态的
WEB框架之MVC/MTV
MVC模式
MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式
Model(模型)表示应用程序核心(比如数据库记录列表)
View(视图)显示数据(数据库记录)
Controller(控制器)处理输入(写入数据库记录)
MVC 模式同时提供了对 HTML、CSS 和 JavaScript 的完全控制。
Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的前端网页。
Controller(控制器)是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据映射,模式渲染等。
MTV模式
MTV(Model Templates Views): Model(模型) -- Templates(模版) --Views(视图) Django是标准的MTV框架。
Django处理顺序
1、wsgi
socket请求处理
2、控制器(django框架本身)
控制用户输入,url匹配,通过映射列表将一个请求发送到一个合适的视图;
3、views --Views
python程序,向模型和模板发送(或获取)数据;
4、模型绑定 --Model
数据库存取数据
5、模板引擎 --Templates
用于将内容与展现分离,描述了数据如何展现(如网页模板);
6、模式渲染 --Views
将模板和数据整合,形成最终网页;
7、控制器(django框架本身)
返回用户展示。
MVC和MTV的区别
MVC即模型-视图-控制器模式,就是为那些需要为同样的数据提供多个视图的应用程序而设计的。它很好地实现了数据层与表示层的分离,特别适用于开发与用户图形界面有关的应用程序。
控制器用来处理用户命令以及程序事件;模型维护数据并提供数据访问方法;视图用于数据的显示。 MTV即模型-模版-视图模式,其标准名称是有争议的。在MVC的解释中,视图描述了展现给用户的数据,是指所看到的数据,而不是如何看见它。在python中视图是指对某一特定URL的回调函数,
因为回调函数描述了所要展现的数据。模版用于将内容与展现分离。在django中,视图描述了要展现的数据,而视图一般转交给模版。模版描述了数据如何展现。控制器则是指django框架本身,
通过URL配置,系统将一个请求发送到一个合适的视图。
转自:http://www.cnblogs.com/daliangtou/p/5258905.html
服务器程序和应用程序
对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。
服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。
应用程序则负责具体的逻辑处理。
为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。
这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器:这就是WSGI以及和WSGI相关的wsgiref
wsgi
什么是WSGI?
WSGI(Web Server Common Interface)是专门为Python语言制定的web服务器与应用程序之间的网关接口规范,通俗的来说,只要一个服务器拥有一个实现了WSGI标准规范的模块(
例如apache的mod_wsgi模块),那么任意的实现了WSGI规范的应用程序都能与它进行交互。因此,WSGI也主要分为两个程序部分:服务器部分和应用程序部分;常用的WSGI服务器有uwsgi、Gunicorn
什么是wsgiref?
wsgiref则是官方给出的一个实现了WSGI标准用于演示用的简单Python内置库,它实现了一个简单的WSGI Server和WSGI Application(在simple_server模块中),主要分为五个模块:
simple_server, util, headers, handlers, validate。
wsgiref由什么用?
我们利用wsgiref模块来替换我们自己写的web框架的socket server部分
wsgiref源码地址:https://pypi.python.org/pypi/wsgiref
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()
jinja2
上面的代码实现了一个简单的动态,我完全可以从数据库中查询数据,然后去替换我html中的对应内容,然后再发送给浏览器完成渲染。 这个过程就相当于HTML模板渲染数据。 本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据。 我这里用的特殊符号是我定义的,其实模板渲染有个现成的工具: jinja2
下载jinja2:
pip install jinja2
<!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>
使用jinja2渲染index2.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的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。
Django
Django是什么
Django是一个开放源代码的Web应用框架,由Python写成。采用了MT‘V的框架模式,即模型M,模板T和视图V。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,
即是CMS(内容管理系统)软件。并于2005年7月在BSD许可证下发布。这套框架是以比利时的吉普赛爵士吉他手Django Reinhardt来命名的。
Django可以干什么
Django工作机制
当访问url的时候,Django会根据ROOT_URLCONF的设置来装载URLConf。 然后按顺序逐个匹配URLConf里的URLpatterns。如果找到则会调用相关联的视图函数,并把HttpRequest对象作为第一个参数(通常是request) 最后该view函数负责返回一个HttpResponse对象。
Django的组成
Django作为一个完善的web框架,主要包含如下几个部分
用于进行数据持久化的ORM模块
用于进行URL地址分配的路由模块
用于进行模板页面处理的模板系统
用于进行表单操作的表单模型
用于进行性能突破的缓存系统
专业术语解释
持久化:数据永久的保存的过程称为数据的持久化
ORM:将程序中的对象[Object]和数据库中的表[Relation]建立关联关系[Mapping]的过程称为ORM
路由:模拟生活中的路由器,将请求URL地址和对应的函数进行关联的操作称为路由
命令安装指定版本(安装最新LTS版):
cmd中输入一下命令即可:
pip3 install django==1.11.9
Pycharm安装Django:
点击>File | Settings | Project: mysite | Project Interpreter |点击最右边的加号 |在弹出页面的搜索框中输入Django |在右下方勾选Specify version选中安装版本|
点击左下方的 Install Package即可完成
Pycharm删除已安装版本:
点击>File | Settings | Project: mysite | Project Interpreter | 右边方框中选中已经安装好的Django | 最右边选中红色减号即可完成删除
命令删除已经安装的版本:
pip uninstall django
创建一个django项目:
首先必须切换到项目的指定位置(将要保存项目多的地方)
cd /d F: (切换到F盘)
执行下面的命令创建了一个名为"mysite"的Django 项目:
django-admin startproject mysite
使用Pycharm创建
File--》New Project-->Django-->(Location项目路径,Interpreter是所安装的Python的版本)-->右下角点击create即可
Django结构目录介绍:
mysite/ ├── manage.py # 管理文件 └── mysite # 项目目录 ├── __init__.py ├── settings.py # 配置 ├── urls.py # 路由 --> URL和函数的对应关系 └── wsgi.py # runserver命令就使用wsgiref模块做简单的web server 和服务器相关的
Django结构分析
Django 程序 Django - 对整个程序进行配置 settings - 配置文件 usls - url对应关系 对应逻辑函数 为每个逻辑函数 分配相应视图 wsgi - 遵循WSIG规范, uwsgi + nginx manage.py - #管理Django 程序
执行的相关命令:
python manage.py
python manage.py startapp 名称 创建APP
数据库迁移:
python manage.py makemigrations
python manage.py migrate
APP migrations 数据库操作记录->相应表结构发生变化,比如说字段类型,并不是增删改成的变动 admin Django 为我们提供的后台管理 apps 配置当前APP models ORM,写指定的类通过命令可以创建数据库结构. tests 单元测试 Views 业务逻辑代码
运行Django项目:
执行manage.py文件
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', ], }, }, ]
配置静态文件
对于静态文件,也就是 我们Templates
存放html
文件视图所对应相关的JS
,CSS
,图片
存放的对方 通常我们在 程序目录下建立一个文件夹->static
(这里写死就可以了,不要换别的名字)
在settings.py
文件下添加如下配置,意思是在视图对应的静态文件会自动在static
目录下检索
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', ]
Djang中Project和APP的区别
project 是 整个项目(网站)
APP是指项目里的某个功能模块,比如user 是一个APP
以下内容为转载部分
Django原理讲解
-
业务流程原理
url请求---->访问路由系统(负责分发请求到相应视图函数)------>视图函数(处理请求)------>DataBase(数据库操作数据生成对应页面返回给用户)
-
底层实现原理
本质是Django就是一个Socket服务端,用户的浏览器其实就是一个Socket客户端.用户访问网站的过程就是服务端与客户端Socket通信的过程
代码实现
import socket
def handle_request(client): buf = client.recv(1024) client.send("HTTP/1.1 200 OK ".encode()) f = open('index.html', 'rb') data = f.read() client.send(data) def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('localhost', 8000)) sock.listen(5) while True: connection, address = sock.accept() handle_request(connection) connection.close() if __name__ == '__main__': main()
Django中请求的生命周期
概述
首先我们知道HTTP请求及服务端响应中传输的所有数据都是字符串.
在Django中,当我们访问一个的url时,会通过路由匹配进入相应的html网页中.
请求生命周期概念
是指当用户在浏览器上输入url到用户看到网页的这个时间段内,Django后台所发生的事情
而Django的生命周期内到底发生了什么呢??
1. 当用户在浏览器中输入url时,浏览器会生成请求头和请求体发给服务端 请求头和请求体中会包含浏览器的动作(action),这个动作通常为get或者post,体现在url之中. 2. url经过Django中的wsgi,再经过Django的中间件,最后url到过路由映射表,在路由中一条一条进行匹配, 一旦其中一条匹配成功就执行对应的视图函数,后面的路由就不再继续匹配了. 3. 视图函数根据客户端的请求查询相应的数据.返回给Django,然后Django把客户端想要的数据做为一个字符串返回给客户端. 4. 客户端浏览器接收到返回的数据,经过渲染后显示给用户.
第一步:浏览器输入网址。接下来你以为就到django的urls了?No,紧接着是要经过django里的settings.py里的MIDDLEWARE配置,也就是中间件。
第二步:中间件通过之后才会到urls,通过urls的配置,找到views里的函数或类
第三步:执行函数或类,返回一个字符串。
第四步:再通过一系列的中间件。
第五步:前端或模板语言获取到字符串,然后解析,在页面上展示出来。
视图函数根据客户端的请求查询相应的数据后.如果同时有多个客户端同时发送不同的url到服务端请求数据
服务端查询到数据后,怎么知道要把哪些数据返回给哪个客户端呢??
因此客户端发到服务端的url中还必须要包含所要请求的数据信息等内容.
例如,http://www.aaa.com/index/?nid=user
这个url中,
客户端通过get请求向服务端发送的nid=user的请求,服务端可以通过request.GET.get("nid")的方式取得nid数据
客户端还可以通过post的方式向服务端请求数据.
当客户端以post的方式向服务端请求数据的时候,请求的数据包含在请求体里,这时服务端就使用request.POST的方式取得客户端想要取得的数据
需要注意的是
request.POST是把请求体的数据转换一个字典,请求体中的数据默认是以字符串的形式存在的.
FBV模式和CBV模式
FBV
一个url对应一个视图函数,这个模式叫做FBV(Function Base Views)
CBV
CBV(Class Base views),即一个url对应一个类
例子:使用cbv模式来请求网页
路由信息:
urlpatterns = [ url(r'^fbv/',views.fbv), url(r'^cbv/',views.CBV.as_view()), ]
视图函数配置:
from django.views import View class CBV(View): def get(self,request): return render(request, "cbv.html") def post(self,request): return HttpResponse("cbv.get")
cbv.html网页的内容:
<body> <form method="post" action="/cbv/"> {% csrf_token %} <input type="text"> <input type="submit"> </form> </body>
启动项目,在浏览器中输入http://127.0.0.1:8000/cbv/
,回车,得到的网页如下:
在input框中输入"hello",后回车,得到的网页如下:
使用fbv的模式,在url匹配成功之后,会直接执行对应的视图函数.
而如果使用cbv模式,在url匹配成功之后,会找到视图函数中对应的类,然后这个类回到请求头中找到对应的Request Method
.
如果是客户端以post的方式提交请求,就执行类中的post方法;
如果是客户端以get的方式提交请求,就执行类中的get方法
然后查找用户发过来的url,然后在类中执行对应的方法查询生成用户需要的数据.
fbv方式请求的过程
用户发送url请求,Django会依次遍历路由映射表中的所有记录,一旦路由映射表其中的一条匹配成功了,
就执行视图函数中对应的函数名,这是fbv的执行流程
cbv方式请求的过程
当服务端使用cbv模式的时候,用户发给服务端的请求包含url和method,这两个信息都是字符串类型
服务端通过路由映射表匹配成功后会自动去找dispatch方法,然后Django会通过dispatch反射的方式找到类中对应的方法并执行
类中的方法执行完毕之后,会把客户端想要的数据返回给dispatch方法,由dispatch方法把数据返回经客户端
例子,把上面的例子中的视图函数修改成如下:
from django.views import View class CBV(View): def dispatch(self, request, *args, **kwargs): print("dispatch......") res=super(CBV,self).dispatch(request,*args,**kwargs) return res def get(self,request): return render(request, "cbv.html") def post(self,request): return HttpResponse("cbv.get")
打印结果:
<HttpResponse status_code=200, "text/html; charset=utf-8"> dispatch...... <HttpResponse status_code=200, "text/html; charset=utf-8">
需要注意的是:
以get方式请求数据时,请求头里有信息,请求体里没有数据
以post请求数据时,请求头和请求体里都有数据.
Django登录实现
- 在自己建立的APP 里的
Views
中添加逻辑函数login
render
这个模块相当于一个open
读取html
文件中的数据返回给浏览器
from django.shortcuts import render,redirect def login(request): # request 包含用户提交的所有信息 error_msg = "" if request.method == 'POST': # 获取用户通过post 提交过来的数据 user = request.POST.get('user',None) pwd = request.POST.get('pwd',None) if user == 'admin' and pwd == "admin": #跳转到响应页面 ("/"相当于前面的地址127.1.0.0:8000) return redirect('/home') else: #用户名密码不匹配 error_msg = "错了 !!!!天啊!!" //error_msg 替换html文件 相同字段文字 return render(request, 'login.html',{'error_msg':error_msg})
2.登录函数逻辑所对应的 templates
文件中的login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>login</title> <style> label{ 80px; text-align: right; display: inline-block; } </style> </head> <body> <form action="/login/" method="post" style="background-color: cadetblue"> <p> <label for="username">用户名</label> <input id="username" name="user" type="text"/> </p> <p> <label for="password">密码</label> <input id="password" name="pwd" type="text"/> <input type="submit" value="提交"> //error_msg 替换请求 相同字段文字 <span style="color: red">{{ error_msg}}</span> </p> </form> </body> </html>
-
示例
页面数据的操作功能
同样的在自己建立的APP 里的Views
中添加逻辑函数 home
USER_LIST = [] # # for index in range(20): # tem = {'username':'雪芙'+str(index),'sex':'女','age':20+index} # USER_LIST.append(tem) def home(request): if request.method == 'POST': #获取用户提交的数据 POST 请求中 u = request.POST.get('username') s = request.POST.get('sex') a = request.POST.get('age') tem = {'username': u, 'sex': s, 'age':a} USER_LIST.append(tem) //当首次没有数据时候,列表为空 return render(request,'home.html',{'user_list':USER_LIST})
- home函数逻辑所对应的
templates
文件中的home.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>home</title> </head> <body style="margin: 0"> <div style="height:48px;background-color: cadetblue"></div> <div> <form action="/home/" method="post"> <input type="text" name="username" placeholder="用户名"/> <input type="text" name="sex" placeholder="性别"/> <input type="text" name="age" placeholder="年龄"/> <input type="submit" value="添加"> </form> </div> <div> <table> //页面中的for循环写法 {% for row in user_list %} <tr> <td>{{ row.username }}</td> <td>{{ row.sex }}</td> <td>{{ row.age }}</td> </tr> {% endfor %} </table> </div> </body> </html>
-
示例
总结
Django的主要目的是简便、快速的开发数据库驱动的网站,Django有许多功能强大的第三方,本质是上是一个服务端的Socket连接,但是功能及其强大,封装后的功能简单易操作,深受广大用户喜爱
.配合上模板页面,数据处理,就可以建立自己的网站或者移动应用程序.
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": ["烫头", "泡吧"]})
redirect
接受一个URL参数,表示跳转到指定的URL。
def index(request): # 业务逻辑代码 return redirect("/home/")
Django基础注意事项
1:get和post的应用场景 访问网站的时候用get向网页提交数据的时候用post 2: 由页面向后端提交数据的时候,使用FORM表单 3:使用form表单注意事项 form表单中必须要写action属性和method属性,如果涉及到提交文件的话必须写enctype属性 4: input标签必须放在form表单中,input标签必须写name属性 5: 提交按钮类型是submit(input type=' submit')
启动Django报错:
Django 启动时报错 UnicodeEncodeError ...
报这个错误通常是因为计算机名为中文,改成英文的计算机名重启下电脑就可以了。