Django知识点整理
web框架?
框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞台来做表演。
web应用 访问请求流程
*浏览器发送一个HTTP请求;
*服务器收到请求,生成一个HTML文档(待补充;是否是全部类型的访问都需要生成文档);
*服务器把HTML文档作为HTTP响应的Body发送给浏览器;
*浏览器收到HTTP响应,从HTTP Body取出HTML文档并解析显示
对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。
socket实例
# -*- coding: utf-8 -*-
# @Date : 2016/11/24
# @Author : Jesson
import socket
def handle_request(client):
buf = client.recv(1024)
client.send("HTTP/1.1 200 OK\r\n\r\n".encode("utf8"))
client.send("<h1 style='color:#1b7665'>Hello, Jesson</h1>".encode("utf8"))
def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost',8001))
sock.listen(5)
whileTrue:
connection, address = sock.accept()
handle_request(connection)
connection.close()
if __name__ =='__main__':
main()</pre>
地址栏输入访问请求:
最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。
如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。
正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。
这个接口就是WSGI:Web Server Gateway Interface。
WSGI 简单介绍
WSGI,全称 Web Server Gateway Interface,或者 Python Web Server Gateway Interface ,是为 Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接口。自从 WSGI 被开发出来以后,许多其它语言中也出现了类似接口。
WSGI 是作为 Web 服务器与 Web 应用程序或应用框架之间的一种低级别的,以提升可移植 Web 应用开发的共同点。WSGI 是基于现存的 CGI 标准而设计的。
很多框架都自带了 WSGI server ,比如 Flask,webpy,Django、CherryPy等等。当然性能都不好,自带的 web server 更多的是测试用途,发布时则使用生产环境的 WSGI server或者是联合 nginx 做 uwsgi。
通俗来讲,WSGI就像是一座桥梁,一边连着web服务器,另一边连着用户的应用。但是呢,这个桥的功能很弱,有时候还需要别的桥来帮忙才能进行处理。
WSGI的作用
WSGI有两方:“服务器”或“网关”一方,以及“应用程序”或“应用框架”一方。服务方调用应用方,提供环境信息,以及一个回调函数(提供给应用程序用来将消息头传递给服务器方),并接收Web内容作为返回值。
所谓的 WSGI中间件同时实现了API的两方,因此可以在WSGI服务和WSGI应用之间起调解作用:从WSGI服务器的角度来说,中间件扮演应用程序,而从应用程序的角度来说,中间件扮演服务器。“中间件”组件可以执行以下功能:
- 重写环境变量后,根据目标URL,将请求消息路由到不同的应用对象。
- 允许在一个进程中同时运行多个应用程序或应用框架。
- 负载均衡和远程处理,通过在网络上转发请求和响应消息。
- 进行内容后处理,例如应用XSLT样式表。
WSGI 的设计确实参考了 Java 的 servlet。http://www.python.org/dev/peps/pep-0333/ 有这么一段话:
By contrast, although Java has just as many web application frameworks available, Java's "servlet" API makes it possible for applications written with any Java web application framework to run in any web server that supports the servlet API.
另外,需要提及的一点是:其它基于python的web框架,如tornado、flask、webpy都是在这个范围内进行增删裁剪的。例如tornado用的是自己的异步非阻塞“wsgi”,flask则只提供了最精简和基本的框架。Django则是直接使用了WSGI,并实现了大部分功能。
WSGI实例一
# -*- coding: utf-8 -*-
from wsgiref.simple_server import make_server
def application(environ, start_response):
start_response('200 OK',[('Content-Type','text/html')])
return[b'<h1 >Hello, web!</h1>']
#''中间为空,表示的是本地地址
httpd = make_server('',8080, application)
print('Serving HTTP on port 8000...')
# 开始监听HTTP请求:
httpd.serve_forever()</pre>
整个application()函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写,
我们只负责在更高层次上考虑如何响应请求就可以了。
application()函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器,我们可以挑选一个来用。
Python内置了一个WSGI服务器,这个模块叫wsgiref
application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数://environ:一个包含所有HTTP请求信息的dict对象; //start_response:一个发送HTTP响应的函数。
在application()函数中,调用:
start_response('200 OK',[('Content-Type','text/html')])
就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数。
start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每
个Header用一个包含两个str的tuple表示。
通常情况下,都应该把Content-Type头发送给浏览器。其他很多常用的HTTP Header也应该发送。
然后,函数的返回值b'<h1>Hello, web!</h1>'将作为HTTP响应的Body发送给浏览器。
有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML,
通过start_response()发送Header,最后返回Body。</pre>
WSGI实例二
from wsgiref.simple_server import make_server
def f1():
f1=open("jd_index1.html","rb")
data1=f1.read()
return[data1]
def f2():
f2=open("tb_index.html","rb")
data2=f2.read()
return[data2]
def application(environ, start_response):
print(environ['PATH_INFO'])
path=environ['PATH_INFO']
start_response('200 OK',[('Content-Type','text/html')])
# 如果URL路径为京东,执行函数1,返回京东主页
if path=="/jingdong":
return f1()
# 如果URL路径为淘宝,执行函数2,返回淘宝主页
elif path=="/taobao":
return f2()
else:
return["<h1>404</h1>".encode("utf8")]
httpd = make_server('',8810, application)
print('Serving HTTP on port 8810...')
# 开始监听HTTP请求:
httpd.serve_forever()
打开浏览器访问相应的路径
这里我们WSGI每次修改完数据后,都需要重新启动该服务。
WSGI实例3 打印当前时间
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2016/11/24
# @Author : Jesson
import time
from wsgiref.simple_server import make_server
def f1(req):
print(req)
print(req["QUERY_STRING"])
f1=open("jd_index1.html","rb")
data1=f1.read()
return[data1]
def f2(req):
f2=open("tb_index.html","rb")
data2=f2.read()
return[data2]
def f3(req):#模版以及数据库
f3=open("current_time.html","rb")
data3=f3.read()
times=time.strftime("%Y-%m-%d %X", time.localtime())
# 在前端相应的页面 设置自定义模版语言'! !'
data3=str(data3,"utf8").replace("!time!",str(times))
return[data3.encode("utf8")]
def routers():
urlpatterns =(
('/jingdong',f1),
('/taobao',f2),
("/cur_time",f3)
)
return urlpatterns
def application(environ, start_response):
print(environ['PATH_INFO'])
path=environ['PATH_INFO']
start_response('200 OK',[('Content-Type','text/html')])
urlpatterns = routers()
func =None
for item in urlpatterns:
if item[0]== path:
func = item[1]
break
if func:
return func(environ)
else:
return["<h1>404</h1>".encode("utf8")]
httpd = make_server('',8828, application)
print('Serving HTTP on port 8828...')
# 开始监听HTTP请求:
httpd.serve_forever()</pre>
其实,上边几个实例就相当于一个简单的web框架。
MVC和MTV设计模式
MVC/MTV介绍
- MVC百度百科:全名Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
- 通俗解释:一种文件的组织和管理形式!不要被缩写吓到了,这其实就是把不同类型的文件放到不同的目录下的一种方法,然后取了个高大上的名字。当然,它带来的好处有很多,比如前后端分离,松耦合等等,就不详细说明了。
模型(model):定义数据库相关的内容,一般放在models.py文件中。
视图(view):定义HTML等静态网页文件相关,也就是那些html、css、js等前端的东西。
控制器(controller):定义业务逻辑相关,就是你的主要代码。
- MTV: 有些WEB框架觉得MVC的字面意思很别扭,就给它改了一下。view不再是HTML相关,而是主业务逻辑了,相当于控制器。html被放在Templates中,称作模板,于是MVC就变成了MTV。这其实就是一个文字游戏,和MVC本质上是一样的,换了个名字和叫法而已,换汤不换药。
Django的MTV模型组织
在web开发的项目文件中,相关目录分开存放,必须要有机制将他们在内里进行耦合。在Django中,urls、orm、static、settings等起着重要的作用。
一个典型的业务流程是如下图所示:
那么我们学Django学的是什么?
1. 目录结构规范
2. urls路由方式
3. settings配置
4. ORM操作
5. 模板渲染
6. 其它
Django工作流程和命令行工具
基本流程
# Django
# 安装: pip3 install django
添加环境变量
#1 创建project工程
django-admin startproject mysite
---mysite
---settings.py
---url.py
---wsgi.py
---manage.py(启动文件)
#2 创建APP应用
python mannage.py startapp app01
#3 settings配置
TEMPLATES
STATICFILES_DIRS=(
os.path.join(BASE_DIR,"statics_自定义"),
)
STATIC_URL ='/static/'
# 我们只能用 STATIC_URL,但STATIC_URL会按着你的STATICFILES_DIRS去找
<script src="/statics_自定义/jquery-3.1.1.js"></script>
------这么写可以实现,但是不规范-----不能直接用,必须用STATIC_URL ='/static/'
<script src="/static/jquery-3.1.1.js"></script>
采用第二种方式,后端的更改不会影响前端的引入,避免造成前端大量修改。
#4 根据需求设计代码
url.py
view.py
#5 使用模版
render(req,"index.html")
#6 启动项目
python manage.py runserver 127.0.0.1:8090
#7 连接数据库,操作数据
model.py
django的命令行工具
django-admin.py 是Django的一个用于管理任务的命令行工具,manage.py是对django-admin.py的简单包装,每一个Django Project里都会有一个mannage.py。
- 创建一个django工程 : django-admin.py startproject mysite
当前目录下会生成mysite的工程,目录结构如下:
- manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库等。
- settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
- urls.py ----- 负责把URL模式映射到应用程序。
- 在mysite目录下创建blog应用: python manage.py startapp blog
-
启动django项目:python manage.py runserver 8080
这样我们的django就启动起来了!当我们访问:http://127.0.0.1:8080/时就可以看到: -
生成同步数据库的脚本文件:python manage.py makemigrations
同步数据库: python manage.py migrate
注意:
在开发过程中,数据库同步误操作之后,难免会遇到后面不能同步成功的情况,解决这个问题的一个简单粗暴方法是把migrations目录下的脚本(除init.py之外)全部删掉,再把数据库删掉之后创建一个新的数据库,数据库同步操作再重新做一遍。
-
当我们访问http://127.0.0.1:8080/admin/时,会出现:
所以我们需要为进入这个项目的后台创建超级管理员:python manage.py createsuperuser 终端执行上述命令,设置好用户名和密码后便可登录啦!
设置用户名的时候,邮箱email可以不用输入,密码有复杂度校验,设置的复杂点。
另外,还会提示你,密码会明文显示。Warning: Password input may be echoed.
OK~ 如此优雅的后台界面! -
清空数据库: python manage.py flush
- 查询某个命令的详细信息: django-admin.py help startapp
admin 是Django 自带的一个后台数据库管理系统。 - 启动交互界面 :python manage.py shell
这个命令和 直接运行 python 进入 shell 的区别是:你可以在这个 shell 里面调用当前项目的 models.py 中的 API,对于操作数据,还有一些小测试非常方便。
######思考题补充:
(1) 如何更改正在运行的开发服务器端口
#进入django命令行,执行:
python manage.py runserver 加上新的端口号8080
Django URL (路由系统)
URL配置(URLconf)就像Django 所支撑网站的目录。
它的本质是URL模式以及要为该URL模式调用的视图函数之间的映射表;你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码。
`urlpatterns =[`
`url(正则表达式, views视图函数,参数,别名),`
`]`
参数说明:
- 一个正则表达式字符串
- 一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
- 可选的要传递给视图函数的默认参数(字典形式)
- 一个可选的name参数
URL conf实例
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns =[
url(r'^articles/2003/$', views.special_case_2003),
#url(r'^articles/[0-9]{4}/$
', views.year_archive),
url(r'^articles/([0-9]{4})/$', views.year_archive), #no_named group
url(r'^articles/([0-9]{4})/([0-9]{2})/$
', views.month_archive),
url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]
Named groups
使用name组 来配置groups
python 测试代码
import re
ret=re.search('(?P<id>\d{3})/(?P<name>\w{3})','weeew34ttt123/ooo')
print(ret.group())
print(ret.group('id'))
print(ret.group('name'))
输出结果:
123/ooo
123
ooo
projects下的urls.py 配置练习:
from django.conf.urls import url
from.import views
urlpatterns =[
# 正则无名函数
url(r'^articles/2003/$', views.special_case_2003),
url(r'^articles/(?P<year>[0-9]{4})/$
', views.year_archive),
url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$
', views.article_detail),
]
url起别名,功能对比static名称
urls.py配置
########################
urlpatterns =[
# 起别名
url(r'^index',views.index,name='bieming'),
url(r'^admin/', admin.site.urls),
]
# views.py文件中 添加index自定义方法
def index(req):
if req.method=='POST':
username=req.POST.get('username')
password=req.POST.get('password')
# 实际中,是需要从数据库中读取数据,这里为了演示效果,直接写成固定的了。
if username=='jesson'and password=='123':
returnHttpResponse("登陆成功")
return render(req,'index.html')
# index.html
#####################
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
# 对应urls.py中的name='bieming'
<form action="{% url 'bieming' %}" method="post">
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
<input type="submit" value="submit">
</form>
</body>
</html>
#######################
Django Views(视图函数)
http请求中产生两个核心对象:
http请求:HttpRequest对象
http响应:HttpResponse对象
所在位置:django.http
之前我们用到的参数request就是HttpRequest
检测方法:isinstance(request,HttpRequest)
HttpRequest对象的属性和方法:
path: 请求页面的全路径,不包括域名
method: 请求中使用的HTTP方法的字符串表示。全大写表示。例如
if req.method=="GET":
do_something()
elseif req.method=="POST":
do_something_else()
GET: 包含所有HTTP GET参数的类字典对象
POST: 包含所有HTTP POST参数的类字典对象
服务器收到空的POST请求的情况也是可能发生的,也就是说,表单form通过HTTP POST方法提交请求,但是表单中可能没有数据,因此不能使用if req.POST来判断是否使用了HTTP POST 方法;应该使用 if req.method=="POST"
COOKIES: 包含所有cookies的标准Python字典对象;keys和values都是字符串。
FILES: 包含所有上传文件的类字典对象;
FILES中的每一个Key都是
<input type="file" name="" />
标签中name属性的值,FILES中的每一个value同时也是一个标准的python字典对象,包含下面三个Keys:
filename: 上传文件名,用字符串表示
content_type: 上传文件的Content Type
content: 上传文件的原始内容
user:
是一个django.contrib.auth.models.User对象,代表当前登陆的用户。如果 访问用户当前没有登陆,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。
你可以通过user的is_authenticated()方法来辨别用户是否登陆:
if req.user.is_authenticated();
只有激活Django中的AuthenticationMiddleware 时该属性才可用session: 唯一可读写的属性,代表当前会话的字典对象;自己有激活Django中的session支持时该属性才可用。
方法
get_full_path(),用来返回包含查询字符串的请求路径。
比如:http://127.0.0.1:8000/index33/?name=123 ,req.get_full_path()得到的结果就是/index33/?name=123
req.path:/index33
HttpResponse对象:
对于HttpRequest对象来说,是由django自动创建的,但是,HttpResponse对象就必须我们自己创建。每个view请求处理方法必须返回一个HttpResponse对象。
HttpResponse类在django.http.HttpResponse
在HttpResponse对象上扩展的常用方法:
页面渲染: render()#推荐使用
render_to_response()#不推荐使用
![](675fc91f-af3c-4daa-903b-126f02da336a_128_files/049cecf2-3e08-4a0f-b823-ec191c21f54e.png)
页面跳转: redirect("路径")
locals():可以直接将函数本地作用域中所有的变量传给模板,
补充render和redirect的区别:
--------url.py
url(r"login", views.login),
url(r"jesson_back", views.jesson_back),
-------views.py
def login(req):
if req.method=="POST":
if1:
# return redirect("/jesson_back/")
name="jesson"
return render(req,"my backend.html",locals())
return render(req,"login.html",locals())
def jesson_back(req):
name="jesson"
return render(req,"my backend.html",locals())
----------------login.html
<form action="/login/" method="post">
<p>姓名<input type="text" name="username"></p>
<p>性别<input type="text" name="sex"></p>
<p>邮箱<input type="text" name="email"></p>
<p><input type="submit" value="submit"></p>
</form>
---------------my backend.html
<h1>用户{{ name }}你好!</h1>
总结: render和redirect的区别:
- 如果 render的页面需要模板语言渲染,需要的将数据库的数据加载到html,那么所有的这一部分除了写在yuan_back的视图函数中,必须还要写在login中,代码重复,没有解耦.
- the most important: url没有跳转到/jesson_back/,而是还在/login/,所以当刷新后又得重新登录.
自己构造response
Template基础
模板系统的介绍
你可能已经注意到我们在例子视图中返回文本的方式有点特别。 也就是说,HTML被直接硬编码在 Python代码之中。