Pyhton-Web框架之【Django】
一、什么是web框架
框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞台来做表演。
对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。
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:red'>Hello, web</h1>".encode("utf8")) def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('localhost',8001)) sock.listen(5) while True: connection, address = sock.accept() handle_request(connection) connection.close() if __name__ == '__main__': main() web应用本质
最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。
如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。
正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。这个接口就是WSGI:Web Server Gateway Interface。即:web服务网关接口。 定义了Web服务器与Web应用(或Web框架)之间的标准接口
Python中wsgiref就是WSGI接口的一个模块,功能相当于apache、nginx
如何实现一个web框架:
from wsgiref.simple_server import make_server #http请求处理函数 def application(environ, start_response): print(environ) #请求信息 start_response('200 OK', [('Content-Type', 'text/html')]) #设置响应头 return [b'<h1>Hello, web!</h1>'] #返回响应体,字符串迭代对象 if __name__ == '__main__': httpd = make_server('127.0.0.1', 8000, application) print ("Serving HTTP on port 8000...") httpd.serve_forever() #监听http请求 step1
整个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。
from wsgiref.simple_server import make_server #http处理函数 def application(environ, start_response): # print(environ) #请求信息 start_response('200 OK', [('Content-Type', 'text/html')]) #设置响应头 with open("index1.html","rb") as f: data = f.read() return [data] #返回响应体 if __name__ == '__main__': httpd = make_server('127.0.0.1', 8000, application) print ("Serving HTTP on port 8000...") httpd.serve_forever() #监听http请求 step2
from wsgiref.simple_server import make_server #http处理函数 def application(environ, start_response): # print(environ) #请求信息 start_response('200 OK', [('Content-Type', 'text/html')]) #设置响应头 if environ["PATH_INFO"] == "/heilong": return [b"<h1>Hello, heilong!</h1>"] #返回响应体 elif environ["PATH_INFO"] == "/xiaohei": return [b"<h1>Hello, xiaohei!</h1>"] else: return [b"404 Not Found"] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8000, application) print ("Serving HTTP on port 8000...") httpd.serve_forever() #监听http请求 step3
from wsgiref.simple_server import make_server def heilong(): with open("heilong.html","rb") as f: data = f.read() return data def xiaohei(): with open("xiaohei.html","rb") as f: data = f.read() return data def route_ctrl(): url_dic = { "/heilong":heilong, "/xiaohei":xiaohei, } return url_dic #http处理函数 def application(environ, start_response): # print(environ) #请求信息 url_path=environ["PATH_INFO"] start_response('200 OK', [('Content-Type', 'text/html')]) #设置响应头 url_patterns=route_ctrl() for item in url_patterns: if item == url_path: func = url_patterns.get(item) return [func()] else: return [b"404 Not Found"] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8000, application) print ("Serving HTTP on port 8000...") httpd.serve_forever() #监听http请求 step4
from wsgiref.simple_server import make_server import time def heilong(req): with open("heilong.html","rb") as f: data = f.read() return data def xiaohei(req): with open("xiaohei.html","rb") as f: data = f.read() return data def showtime(req): with open("showtime.html","rb") as f: data = f.read().decode("utf-8") data = data.replace("{{time}}",str(time.ctime())) return data.encode("utf-8") def route_ctrl(): url_dic = { "/heilong":heilong, "/xiaohei":xiaohei, "/showtime":showtime, } return url_dic #http处理函数 def application(environ, start_response): # print(environ) #请求信息 url_path=environ["PATH_INFO"] start_response('200 OK', [('Content-Type', 'text/html')]) #设置响应头 url_patterns=route_ctrl() for item in url_patterns: if item == url_path: func = url_patterns.get(item) return [func(environ)] else: return [b"404 Not Found"] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8000, application) print ("Serving HTTP on port 8000...") httpd.serve_forever() #监听http请求 step5
二、MVC和MTV模式
1.著名的MVC模式:所谓MVC就是把web应用分为模型(M),控制器(C),视图(V)三层;他们之间以一种插件似的,解耦合的方式连接在一起。
2.模型负责业务对象与数据库的对象(ORM),视图负责与用户的交互(页面),控制器(C)接受用户的输入调用模型和视图完成用户的请求。
Django的MTV模式本质上与MVC模式没有什么差别,也是各组件之间为了保持松耦合关系,只是定义上有些许不同,Django的MTV分别代表:
Model(模型):负责业务对象与数据库的对象(ORM)
Template(模版):负责如何把页面展示给用户
View(视图):负责业务逻辑,并在适当的时候调用Model和Template
此外,Django还有一个url分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template
Django WEB框架
三、Django框架的流程与命令行工具
Django实现流程:
django #安装: pip3 install django 添加环境变量 #1 创建project django-admin startproject mysite ---mysite ---settings.py ---url.py ---wsgi.py ---- manage.py(启动文件) #2 创建APP python manage.py startapp app01 pycharm实现创建项目和应用:File——》New Project——》Django Location:项目路径及名称 Application name:应用名 #3 settings配置 TEMPLATES STATICFILES_DIRS=( os.path.join(BASE_DIR,"statics"), ) STATIC_URL = '/static/' # 我们只能用 STATIC_URL,但STATIC_URL会按着你的STATICFILES_DIRS去找#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。
<1> 创建一个django工程 : django-admin.py startproject mysite
当前目录下会生成mysite的工程,目录结构如下:
- manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库等。
- settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
- urls.py ----- 负责把URL模式映射到应用程序。
<2>在mysite目录下创建blog应用: python manage.py startapp blog
<3>启动django项目:python manage.py runserver 8080
这样我们的django就启动起来了!当我们访问:http://127.0.0.1:8080/时就可以看到:
<4>生成同步数据库的脚本:python manage.py makemigrations
同步数据库: python manage.py migrate
四、Django的配置文件settings.py
1、STATIC_ROOT和STATIC_URL
STATIC主要指的是如css,js,images这样文件,在settings里面可以配置STATIC_ROOT和STATIC_URL, 配置方式与MEDIA_ROOT是一样的,但是要注意 #STATIC文件一般保存在以下位置: #1、STATIC_ROOT:在settings里面设置,一般用来放一些公共的js,css,images等。 #2、app的static文件夹,在每个app所在文夹均可以建立一个static文件夹,然后当运行collectstatic时, # Django会遍历INSTALL_APPS里面所有app的static文件夹,将里面所有的文件复制到STATIC_ROOT。因此, # 如果你要建立可复用的app,那么你要将该app所需要的静态文件放在static文件夹中。 # 也就是说一个项目引用了很多app,那么这个项目所需要的css,images等静态文件是分散在各个app的static文件的,比 # 较典型的是admin应用。当你要发布时,需要将这些分散的static文件收集到一个地方就是STATIC_ROOT。 #3、STATIC文件还可以配置STATICFILES_DIRS,指定额外的静态文件存储位置。 # STATIC_URL的含义与MEDIA_URL类似。 # ---------------------------------------------------------------------------- #注意1: #为了后端的更改不会影响前端的引入,避免造成前端大量修改 STATIC_URL = '/static/' #引用名 STATICFILES_DIRS = ( os.path.join(BASE_DIR,"statics") #实际名 ,即实际文件夹的名字 ) #django对引用名和实际名进行映射,引用时,只能按照引用名来,不能按实际名去找 #<script src="/statics/jquery-3.1.1.js"></script> #------error-----不能直接用,必须用STATIC_URL = '/static/': #<script src="/static/jquery-3.1.1.js"></script> #注意2(statics文件夹写在不同的app下,静态文件的调用): STATIC_URL = '/static/' STATICFILES_DIRS=( (os.path.join(BASE_DIR,"app01","statics")) , ) #html中引用方式一 #<script src="/static/jquery-3.1.1.js"></script> #注意3: STATIC_URL = '/static/' #html中引用方式二 {% load staticfiles %} # <script src={% static "jquery-3.1.1.js" %}></script>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> {% load staticfiles %} </head> <body> <h1>当前时间:{{time}}</h1> {#<script src="/static/jquery-3.1.1.js"></script>#} <script src="{% static 'jquery-3.1.1.js' %}"></script> <script> $("h1").css("color","red") </script> </body> </html>
def showtime(request): # return HttpResponse("hello django") t = time.ctime() #{"time":t}将t变量值渲染为html文件中的{{time}},在后端渲染之后传给前端 return render(request,"showtime.html",{"time":t})
五、Django的URL(路由系统)
URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL模式以及要为该URL模式调用的视图函数之间的映射表;你就是以这种方式告诉Django,对应这个URL调用这段代码。
urlpatterns = [
url(正则表达式, views视图函数,参数,别名),
]
参数说明:
- 一个正则表达式字符串
- 一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
- 可选的要传递给视图函数的默认参数(字典形式)
- 一个可选的name参数
from django.conf.urls import url,include from django.contrib import admin from showtime import views #showtime是当前应用名 urlpatterns = [ url(r'^admin/', admin.site.urls), url(r"^showtime/$",views.showtime), #分组,一个()为一个组,做一个参数传递给views.tupletest(y,m) url(r"^tupletest/(\d{4})/(\d{2})",views.tupletest), #分组后,?<变量名>指定,做为views.tupletest(request,year,month)的参数名,必需与指定的一致 url(r"^tupletest/(?P<year>\d{4})/(?P<month>\d{2})$",views.tupletest), url(r"^register/",views.register), #name指定别名 url(r"^register/",views.register,name="reg"), ] 一个简单的URL配置
上述URL配置中,name指定了别名后,相对应的视图函数所对应的html文件中:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> {% load staticfiles %} //Template模板 </head> <body> <h1>注册信息</h1> <form action="{% url 'reg' %}" method="post"> //按别名指定URL <p>用户名:<input type="text" name = "user"></p> <p>年龄:<input type="text" name = "age"></p> <p>爱好:<input type="checkbox" name = "hobby" value="游泳">游泳 <input type="checkbox" name = "hobby" value="听音乐">听音乐 <input type="checkbox" name = "hobby" value="跑步">跑步 </p> <p><input type="submit"></p> </form> </body> </html>
URL的分发:
from django.conf.urls import url,include from django.contrib import admin from showtime import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r"^showtime/$",views.showtime), #分发后这里URL必需用$指定以什么结尾,否则分发后的URL都将访问views.showtime url(r"^showtime/",include("showtime.urls")), #URL分发 ] URL分发
from django.conf.urls import url,include from showtime import views urlpatterns = [ # url(r"^tupletest/(\d{4})/(\d{2})",views.tupletest), url(r"tupletest/(?P<year>\d{4})/(?P<month>\d{2})$",views.tupletest), # url(r"^register/",views.register), url(r"register/",views.register,name="reg"), ] #注意:访问时,url路径前必需showtime,即相应的应用目录 分发到相应的应用中,而不在全局urls中
六、Django的views(视图函数)
http请求中产生两个核心对象:
http请求:HttpRequest对象
http响应:HttpResponse对象
所在位置:django.http
之前我们用到的参数request就是HttpRequest 检测方法:isinstance(request,HttpRequest)
1、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
2 HttpResponse对象:
对于HttpRequest对象来说,是由django自动创建的,但是,HttpResponse对象就必须我们自己创建。每个view请求处理方法必须返回一个HttpResponse对象。
HttpResponse类在django.http.HttpResponse
在HttpResponse对象上扩展的常用方法:
页面渲染: render()(推荐)<br> render_to_response(), #与render()区别是不用写request参数 页面跳转: redirect("路径") #达到某条件时跳转到另一个页面,注:参数必须是“路径”,即URL,而非HTML模板 #与render("html模板")区别,redirect会改变url,而render不会改变url,重新刷新不会保留当前页面, locals(): 可以直接将函数中所有的变量传给模板 注:request的属性也都可以传递过去
七、Template基础
模板系统介绍
你可能已经注意到我们在例子视图中返回文本的方式有点特别。 也就是说,HTML被直接硬编码在 Python代码之中。
def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
尽管这种技术便于解释视图是如何工作的,但直接将HTML硬编码到你的视图里却并不是一个好主意。 让我们来看一下为什么:
-
对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多。
-
Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。 设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作。
-
程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。
基于这些原因,将页面的设计和Python的代码分离开会更干净简洁更容易维护。 我们可以使用 Django的 模板系统 (Template System)来实现这种模式,这就是本章要具体讨论的问题。
1、模板的组成
组成:HTML代码+逻辑控制代码
2、逻辑控制代码的组成
变量:(使用双大括号来引用变量):
语法:
{{var_name}}
Template对象和Context对象
Template:模板中如{{}}、{%%}
Context:上下文,如locals()、{"name":user}
#cmd 中如下测试: >>> python manange.py shell #(进入该django项目的环境) >>> from django.template import Context, Template >>> t = Template('My name is {{ name }}.') >>> c = Context({'name': 'Stephane'}) >>> t.render(c) 'My name is Stephane.' # 同一模板,多个上下文,一旦有了模板对象,你就可以通过它渲染多个context,无论何时我们都可以 # 像这样使用同一模板源渲染多个context,只进行 一次模板创建然后多次调用render()方法渲染会更为高效 # Low for name in ('John', 'Julie', 'Pat'): t = Template('Hello, {{ name }}') print t.render(Context({'name': name})) # Good t = Template('Hello, {{ name }}') for name in ('John', 'Julie', 'Pat'): print t.render(Context({'name': name})) #推荐使用 return render(request,"index.html",locals())
变量:深度查找(万能的句点符号)在到目前为止的例子中,我们通过 context 传递的简单参数值主要是字符串,然而,模板系统能够非常简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。
from django.shortcuts import render # Create your views here. class People(): def __init__(self,name,sex): self.name = name self.sex = sex def learn(self): print("学习。。。") return "正在学习!" def templatevar(request): li = ["heilong","xiaolong","xiaohei"] dic = {"name":"heilong","age":20,"gender":"男"} p1 = People("heilong","男") return render(request,"Template_var_deep.html",locals()) views.py
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h2>{{ li.0 }}</h2> <!--heilong--> <h2>{{ li.1 }}</h2> <!--xiaolong--> <h2>{{ li.2 }}</h2> <!--xiaohei--> <hr> <h2>{{ dic.name }}</h2> <!--heilong--> <h2>{{ dic.age }}</h2> <!--20--> <h2>{{ dic.gender }}</h2> <!--男--> <hr> <h2>{{ p1.name }}</h2> <!--heilong--> <h2>{{ p1.sex }}</h2> <!--男--> <!-- 如果调用方法,则返回函数的返回值。注:函数不能有参数,没什么意义--> <h2>{{ p1.learn }}</h2> <!--正在学习!--> </body> </html> Template_var_deep.html
变量的过滤器(filter)的使用{{obj|filter:param}}
# 1 add : 给变量加上相应的值 # # 2 addslashes : 给变量中的引号前加上斜线 # # 3 capfirst : 首字母大写 # # 4 cut : 从字符串中移除指定的字符 # # 5 date : 格式化日期字符串 # # 6 default : 如果值是False,就替换成设置的默认值,否则就是用本来的值 # # 7 default_if_none: 如果值是None,就替换成设置的默认值,否则就使用本来的值
标签(tag)的使用(使用大括号和百分比的组合来表示使用tag)
{% if %} 的使用
{% if %}标签计算一个变量值,如果是“true”,即它存在、不为空并且不是false的boolean值,系统则会显示{% if %}和{% endif %}间的所有内容
{% if num >= 100 and 8 %} {% if num > 200 %} <p>num大于200</p> {% else %} <p>num大于100小于200</p> {% endif %} {% elif num < 100%} <p>num小于100</p> {% else %} <p>num等于100</p> {% endif %} {% if %} 标签接受and,or或者not来测试多个变量值或者否定一个给定的变量 {% if %} 标签不允许同一标签里同时出现and和or,否则逻辑容易产生歧义,例如下面的标签是不合法的: {% if obj1 and obj2 or obj3 %}
{% for %}的使用
{% for %}标签允许你按顺序遍历一个序列中的各个元素,每次循环模板系统都会渲染{% for %}和{% endfor %}之间的所有内容
<ul> {% for obj in list %} <li>{{ obj.name }}</li> {% endfor %} </ul> #在标签里添加reversed来反序循环列表: {% for obj in list reversed %} ... {% endfor %} #{% for %}标签可以嵌套: {% for country in countries %} <h1>{{ country.name }}</h1> <ul> {% for city in country.city_list %} <li>{{ city }}</li> {% endfor %} </ul> {% endfor %} #系统不支持中断循环,系统也不支持continue语句,{% for %}标签内置了一个forloop模板变量, #这个变量含有一些属性可以提供给你一些关于循环的信息 1,forloop.counter表示循环的次数,它从1开始计数,第一次循环设为1: {% for item in todo_list %} <p>{{ forloop.counter }}: {{ item }}</p> {% endfor %} 2,forloop.counter0 类似于forloop.counter,但它是从0开始计数,第一次循环设为0 3,forloop.revcounter 4,forloop.revcounter0 5,forloop.first当第一次循环时值为True,在特别情况下很有用: {% for object in objects %} {% if forloop.first %}<li class="first">{% else %}<li>{% endif %} {{ object }} </li> {% endfor %} # 富有魔力的forloop变量只能在循环中得到,当模板解析器到达{% endfor %}时forloop就消失了 # 如果你的模板context已经包含一个叫forloop的变量,Django会用{% for %}标签替代它 # Django会在for标签的块中覆盖你定义的forloop变量的值 # 在其他非循环的地方,你的forloop变量仍然可用 #{% empty %} {{li }} {% for i in li %} <li>{{ forloop.counter0 }}----{{ i }}</li> {% empty %} <li>this is empty!</li> {% endfor %} # [11, 22, 33, 44, 55] # 0----11 # 1----22 # 2----33 # 3----44 # 4----55
{%csrf_token%}:csrf_token标签
用于生成csrf_token的标签,用于防治跨站攻击验证。注意如果你在view的index里用的是render_to_response方法,不会生效
其实,这里是会生成一个input标签,和其他表单标签一起提交给后台的。
{% url %}: 引用路由配置的地址
<form action="{% url "bieming"%}" > <input type="text"> <input type="submit"value="提交"> {%csrf_token%} </form>
{% with %}:用更简单的变量名替代复杂的变量名
{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} #注:只能在with与endwith之间用 {% endwith %}
{% verbatim %}: 禁止render
{% verbatim %} {{ hello }} #这里不会被Template渲染,只返回{{ hello }}内容 {% endverbatim %}
{% load %}: 加载标签库
自定义filter和simple_tag
a、在app中创建templatetags模块(必须的)
b、创建任意 .py 文件,如:my_tags.py
from django import template from django.utils.safestring import mark_safe register = template.Library() #register的名字是固定的,不可改变 @register.filter def filter_multi(v1,v2): return v1 * v2 @register.simple_tag def simple_tag_multi(v1,v2): return v1 * v2 @register.simple_tag def my_input(id,arg): result = "<input type='text' id='%s' class='%s' />" %(id,arg,) return mark_safe(result)
c、在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py :{% load my_tags %}
d、使用simple_tag和filter(如何调用)
-------------------------------.html {% load xxx %} #首行 # num=12 {{ num|filter_multi:2 }} #24 {{ num|filter_multi:"[22,333,4444]" }} {% simple_tag_multi 2 5 %} 参数不限,但不能放在if for语句中 {% simple_tag_multi num 5 %}
e、在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.
注:filter与simple_tag区别
filter可以用在if等语句后,simple_tag不可以
filter只能传一个参数,而simple_tag不限
在simple_tag中也可以引用变量,只是不需要加{{}}
extend模板继承
为了解决网站中,公共页面的代码重复与冗余
extend(继承)模板标签
到目前为止,我们的模板范例都只是些零星的 HTML 片段,但在实际应用中,你将用 Django 模板系统来创建整个 HTML 页面。 这就带来一个常见的 Web 开发问题: 在整个网站中,如何减少共用页面区域(比如站点导航)所引起的重复和冗余代码?
解决该问题的传统做法是使用 服务器端的 includes ,你可以在 HTML 页面中使用该指令将一个网页嵌入到另一个中。 事实上, Django 通过刚才讲述的 {% include %} 支持了这种方法。 但是用 Django 解决此类问题的首选方法是使用更加优雅的策略—— 模板继承 。
本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。
step1:定义基础模板
父模板中添加模板标签
{% block 名称 %}
{% endblock %}
step2:修改子模板
{% extends "基础模板" %} #必须写在首行 {% block 名称 %} #名称必须与父模板中一致,多个的话不能重复 要修改的内容 {% endblock %}
注意:
子模板中{% extends %} 必须写在第一行,否则模板继承不起作用
一般来说,{% block %} 越多越好,这样比较灵活
不允许在同一个模板中定义多个同名的{% block %}
具体来看一个例子:
#urls.py from django.conf.urls import url from django.contrib import admin from manager_system import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r"^index/",views.index), url(r"^student/",views.student),
#views.py from django.shortcuts import render # Create your views here. def index(request): return render(request,"index.html") def student(request): li = ["heilong","xiaolong","xiaohei"] return render(request,"student.html",locals())
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> {% block styles %} {% endblock %} <style> *{ margin: 0; padding: 0; } .title{ width: 100%; height: 40px; background-color: #7259ff; line-height: 40px; text-align: center; color: white; font-size: 20px; } .left{ width: 20%; min-height: 600px; background-color: darkgray; } .manager{ text-align: center; padding: 20px 0; font-size: 25px; } .content{ width: 80%; min-height: 600px; text-align: center; } .left,.content{ float: left; } </style> </head> <body> <div class="outer"> <div class="title">标题</div> <div class="left"> <div class="student manager"><a href="/student">学生管理</a></div> <div class="teacher manager"><a href="">老师管理</a></div> <div class="course manager"><a href="">课程管理</a></div> <div class="classes manager"><a href="">班级管理</a></div> </div> <div class="content"> {% block content %} <h1>欢迎登录管理系统</h1> {% endblock %} {% include "thanks.html" %} </div> </div> </body> </html>
{% extends "index.html" %} {% block styles %} <style> .stu_mes{ color: red; } </style> {% endblock %} {% block content %} {{ block.super }} {% for item in li %} <h1 class="stu_mes">{{ forloop.counter }} : {{ item }}</h1> {% endfor %} {% endblock %}
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h2>感谢使用</h2> </body> </html>
上述例子中还用到一个{% include "thanks.html" %}
即将一个现有的模板添加到另一个模板中
八、Models(数据库模型)
1、数据库的配置
django默认支持sqlite,mysql, oracle,postgresql数据库。
<1> sqlite
django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqlite3
<2> mysql
引擎名称:django.db.backends.mysql
mysql驱动程序
MySQLdb(mysql python)
mysqlclient
MySQL
PyMySQL(纯python的mysql驱动程序)
在django的项目中会默认使用sqlite数据库,在settings里有如下设置
创建表的启动命令:
python manage.py makemigrations
python manage.py migrate
/***admin创建用户****/
python manage.py createsuperuser
# Database # https://docs.djangoproject.com/en/1.10/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', #数据库引擎 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), #数据库名 } }
如果我们想要更改数据库,需要修改如下
# Database # https://docs.djangoproject.com/en/1.10/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', #数据库引擎 'NAME': 'heilong', #你的数据库名称 'USER': 'root', #你的数据库用户名 'PASSWORD': '123456', #你的数据库密码 'HOST': '192.168.189.130', #你的数据库主机,留空默认为localhost 'PORT': '3306', #你的数据库端口 } }
#注意 NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建 USER和PASSWORD分别是数据库的用户名和密码。 设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。 然后,启动项目,会报错:no module named MySQLdb 这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是PyMySQL 所以,我们只需要找到项目名文件下的__init__,在里面写入: import pymysql pymysql.install_as_MySQLdb()
2、ORM对象关系映射
用于实现面向对象编程语言里不同类型系统的数据之间的转换,换言之,就是用面向对象的方式去操作数据库的创建表以及增删改查等操作。
ORM: 一个类对应一个表,类属性对应字段;一个实例对象对应一条记录
表与表之间的关系(两张表):
一对一:利用外键约束并且创建唯一约束(unique)
一对多:利用外键约束实现
多对多:另外创建一个表用来存放两个表之间的关系(还是利用外键约束),本质就是两个一对多关系
优点: 1 ORM使得我们的通用数据库交互变得简单易行,而且完全不用考虑该死的SQL语句。快速开发。
2 可以避免一些新手程序猿写sql语句带来的性能问题。
缺点:1 性能有所牺牲,不过现在的各种ORM框架都在尝试各种方法,比如缓存,延迟加载登来减轻这个问题。效果很显著。
2 对于个别复杂查询,ORM仍然力不从心,为了解决这个问题,ORM一般也支持写raw sql。
3 通过QuerySet的query属性查询对应操作的sql语句
author_obj=models.Author.objects.filter(id=2) print(author_obj.query)
Django操作数据库步骤:
1、数据库配置:修改DATABASES={...}
2、修改项目文件下的__init__.py
import pymysql pymysql.install_as_MySQLdb()
3、models.py中创建类(表)
#例 from django.db import models # Create your models here. class book(models.Model): name=models.CharField(max_length=20) price=models.IntegerField() pub_date=models.DateField()
4、根据类生成迁移文件,命令如下:
#Terminal终端中 python manager.py makemigrations
5、将迁移文件写入到数据库,转换为对应的表,命令如下:
#Terminal终端中 python manager.py migrate
分析代码:
<1> 每个数据模型都是django.db.models.Model的子类,它的父类Model包含了所有必要的和数据库交互的方法。并提供了一个简介漂亮的定义数据库字段的语法。
<2> 每个模型相当于单个数据库表(多对多关系例外,会多生成一张关系表),每个属性也是这个表中的字段。属性名就是字段名,它的类型(例如CharField)相当于数据库的字段类型(例如varchar)。大家可以留意下其它的类型都和数据库里的什么字段对应。
<3> 模型之间的三种关系:一对一,一对多,多对多。
一对一:实质就是在主外键(author_id就是foreign key)的关系基础上,给外键加了一个UNIQUE=True的属性;
一对多:就是主外键关系;(foreign key)
多对多:(ManyToManyField) 自动创建第三张表(当然我们也可以自己创建第三张表:两个foreign key)
<4> 模型常用的字段类型参数
<1> CharField #字符串字段, 用于较短的字符串. #CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数. <2> IntegerField #用于保存一个整数. <3> FloatField # 一个浮点数. 必须 提供两个参数: # # 参数 描述 # max_digits 总位数(不包括小数点和符号) # decimal_places 小数位数 # 举例来说, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段: # # models.FloatField(..., max_digits=5, decimal_places=2) # 要保存最大值一百万(小数点后保存10位)的话,你要这样定义: # # models.FloatField(..., max_digits=19, decimal_places=10) # admin 用一个文本框(<input type="text">)表示该字段保存的数据. <4> AutoField # 一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段; # 自定义一个主键:my_id=models.AutoField(primary_key=True) # 如果你不指定主键的话,系统会自动添加一个主键字段到你的 model. <5> BooleanField # A true/false field. admin 用 checkbox 来表示此类字段. <6> TextField # 一个容量很大的文本字段. # admin 用一个 <textarea> (文本区域)表示该字段数据.(一个多行编辑框). <7> EmailField # 一个带有检查Email合法性的 CharField,不接受 maxlength 参数. <8> DateField # 一个日期字段. 共有下列额外的可选参数: # Argument 描述 # auto_now 当对象被保存时,自动将该字段的值设置为当前时间.通常用于表示 "last-modified" 时间戳. # auto_now_add 当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间. #(仅仅在admin中有意义...) <9> DateTimeField # 一个日期时间字段. 类似 DateField 支持同样的附加选项. <10> ImageField # 类似 FileField, 不过要校验上传对象是否是一个合法图片.#它有两个可选参数:height_field和width_field, # 如果提供这两个参数,则图片将按提供的高度和宽度规格保存. <11> FileField # 一个文件上传字段. #要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime #formatting, #该格式将被上载文件的 date/time #替换(so that uploaded files don't fill up the given directory). # admin 用一个<input type="file">部件表示该字段保存的数据(一个文件上传部件) . #注意:在一个 model 中使用 FileField 或 ImageField 需要以下步骤: #(1)在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件. # (出于性能考虑,这些文件并不保存到数据库.) 定义MEDIA_URL 作为该目录的公共 URL. 要确保该目录对 # WEB服务器用户帐号是可写的. #(2) 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django # 使用 MEDIA_ROOT 的哪个子目录保存上传文件.你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT). # 出于习惯你一定很想使用 Django 提供的 get_<#fieldname>_url 函数.举例来说,如果你的 ImageField # 叫作 mug_shot, 你就可以在模板中以 {{ object.#get_mug_shot_url }} 这样的方式得到图像的绝对路径. <12> URLField # 用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在( 即URL是否被有效装入且 # 没有返回404响应). # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框) <13> NullBooleanField # 类似 BooleanField, 不过允许 NULL 作为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项 # admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据. <14> SlugField # "Slug" 是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.#它们通常用于URLs # 若你使用 Django 开发版本,你可以指定 maxlength. 若 maxlength 未指定, Django 会使用默认长度: 50. #在 # 以前的 Django 版本,没有任何办法改变50 这个长度. # 这暗示了 db_index=True. # 它接受一个额外的参数: prepopulate_from, which is a list of fields from which to auto-#populate # the slug, via JavaScript,in the object's admin form: models.SlugField # (prepopulate_from=("pre_name", "name"))prepopulate_from 不接受 DateTimeFields. <15> XMLField #一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema #的文件系统路径. <16> FilePathField # 可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的. # 参数 描述 # path 必需参数. 一个目录的绝对文件系统路径. FilePathField 据此得到可选项目. # Example: "/home/images". # match 可选参数. 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名. # 注意这个正则表达式只会应用到 base filename 而不是 # 路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif. # recursive可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的全部子目录. # 这三个参数可以同时使用. # match 仅应用于 base filename, 而不是路径全名. 那么,这个例子: # FilePathField(path="/home/images", match="foo.*", recursive=True) # ...会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif <17> IPAddressField # 一个字符串形式的 IP 地址, (i.e. "24.124.1.30"). <18># CommaSeparatedIntegerField # 用于存放逗号分隔的整数值. 类似 CharField, 必须要有maxlength参数.
<5> Field重要参数
<1> null : 数据库中字段是否可以为空 <2> blank: django的 Admin 中添加数据时是否可允许空值 <3> default:设定缺省值 <4> editable:如果为假,admin模式下将不能改写。缺省为真 <5> primary_key:设置主键,如果没有设置django创建表时会自动加上: id = meta.AutoField('ID', primary_key=True) primary_key=True implies blank=False, null=False and unique=True. Only one primary key is allowed on an object. <6> unique:数据唯一 <7> verbose_name Admin中字段的显示名称 <8> validator_list:有效性检查。非有效产生 django.core.validators.ValidationError 错误 <9> db_column,db_index 如果为真将为此字段创建索引 <10>choices:一个用来选择值的2维元组。第一个值是实际存储的值,第二个用来方便进行选择。 如SEX_CHOICES= (( ‘F’,'Female’),(‘M’,'Male’),) gender = models.CharField(max_length=2,choices = SEX_CHOICES)
3、表的操作(增删改查)
表记录的添加:
def add(request): #方式一: b = book(name = "python",price=99,pub_date="1919-12-1") b.save() b = book(name="linux", price=88, pub_date="2000-12-1") b.save() b = book(name="django", price=22, pub_date="2222-1-1") b.save() # 方式二: book.objects.create(name="php", price=44, pub_date="1111-1-1") return HttpResponse("添加成功")
表记录的修改:
def modify(request): # 方式一:推荐使用 book.objects.filter(name="php").update(price=444) # 方式二: b = book.objects.get(name="python") #book.objects.get(name="python")是一个model对象,只能取出一条记录,多条则报错 b.price = 1212 b.save() # 方式三: b = book.objects.filter(name="python")[0] #book.objects.filter(name="python")是一个QuerySet类型,是一个集合 b.price = 1213 b.save() return HttpResponse("修改成功")
表记录的删除:
def delete(request): book.objects.filter(name="php").delete() return HttpResponse("删除成功")
九、admin
可参考的学习地址:
https://www.cnblogs.com/wumingxiaoyao/p/6928297.html
https://docs.djangoproject.com/en/1.11/ref/contrib/admin/actions/
django amdin是django提供的一个后台管理页面,改管理页面提供完善的html和css,使得你在通过Model创建完数据库表之后,就可以对数据进行增删改查,而使用django admin 则需要以下步骤:
- 创建后台管理员
- 配置url
- 注册和配置django admin后台管理页面
注:不建议新手经常使用admin,会形成依赖,核心的是model模块的操作!
1、创建后台管理员
python manage.py createsuperuser
2、配置后台管理url(默认已配)
url(r'^admin/', include(admin.site.urls))
3、注册和配置django admin 后台管理页面
a、在admin中执行如下配置
from django.contrib import admin from app01 import models admin.site.register(models.UserType) admin.site.register(models.UserInfo) admin.site.register(models.UserGroup) admin.site.register(models.Asset)
b、设置数据表名称
class UserType(models.Model): name = models.CharField(max_length=50) class Meta: verbose_name = '用户类型' verbose_name_plural = '用户类型'
c、打开表之后,设定默认显示,需要在model中作如下配置
class UserType(models.Model): name = models.CharField(max_length=50) def __unicode__(self): # python3 is __str__(self) return self.name
d、为数据表添加搜索功能
from django.contrib import admin from app01 import models class UserInfoAdmin(admin.ModelAdmin): list_display = ('username', 'password', 'email') search_fields = ('username', 'email') admin.site.register(models.UserType) admin.site.register(models.UserInfo,UserInfoAdmin) admin.site.register(models.UserGroup) admin.site.register(models.Asset)
e、添加快速过滤
from django.contrib import admin from app01 import models class UserInfoAdmin(admin.ModelAdmin): list_display = ('username', 'password', 'email') search_fields = ('username', 'email') list_filter = ('username', 'email') admin.site.register(models.UserType) admin.site.register(models.UserInfo,UserInfoAdmin) admin.site.register(models.UserGroup) admin.site.register(models.Asset)
十,分页
十一,缓存
由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。
Django中提供了6种缓存方式:
- 开发调试
- 内存
- 文件
- 数据库
- Memcache缓存(python-memcached模块)
- Memcache缓存(pylibmc模块)
1、配置
a、开发调试
# 此为开始调试用,实际内部不做任何操作 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎 'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期) 'OPTIONS':{ 'MAX_ENTRIES': 300, # 最大缓存个数(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) }, 'KEY_PREFIX': '', # 缓存key的前缀(默认空) 'VERSION': 1, # 缓存key的版本(默认1) 'KEY_FUNCTION' 函数名 # 生成key的函数(默认函数会生成为:【前缀:版本:key】) } } # 自定义key def default_key_func(key, key_prefix, version): """ Default function to generate keys. Constructs the key used by all other methods. By default it prepends the `key_prefix'. KEY_FUNCTION can be used to specify an alternate function with custom key making behavior. """ return '%s:%s:%s' % (key_prefix, version, key) def get_key_func(key_func): """ Function to decide which key function to use. Defaults to ``default_key_func``. """ if key_func is not None: if callable(key_func): return key_func else: return import_string(key_func) return default_key_func
b、内存
# 此缓存将内容保存至内存的变量中 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake', } } # 注:其他配置同开发调试版本
c、文件
# 此缓存将内容保存至文件 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': '/var/tmp/django_cache', } } # 注:其他配置同开发调试版本
d、数据库
# 此缓存将内容保存至数据库 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'my_cache_table', # 数据库表 } } # 注:执行创建表命令 python manage.py createcachetable
e、Memcache缓存(python-memcached模块)
# 此缓存使用python-memcached模块连接memcache CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': 'unix:/tmp/memcached.sock', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ] } }
f、Memcache缓存(pylibmc模块)
# 此缓存使用pylibmc模块连接memcache CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': '127.0.0.1:11211', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': '/tmp/memcached.sock', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ] } }
g. Redis缓存(依赖:pip3 install django-redis)
CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "CONNECTION_POOL_KWARGS": {"max_connections": 100} # "PASSWORD": "密码", } } }
from django_redis import get_redis_connection conn = get_redis_connection("default")
2、应用
a. 全站使用
使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存 MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', # 其他中间件... 'django.middleware.cache.FetchFromCacheMiddleware', ] CACHE_MIDDLEWARE_ALIAS = "" CACHE_MIDDLEWARE_SECONDS = "" CACHE_MIDDLEWARE_KEY_PREFIX = ""
b. 单独视图缓存
方式一: from django.views.decorators.cache import cache_page @cache_page(60 * 15) def my_view(request): ... 方式二: from django.views.decorators.cache import cache_page urlpatterns = [ url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)), ]
c、局部视图使用
a. 引入TemplateTag {% load cache %} b. 使用缓存 {% cache 5000 缓存key %} 缓存内容 {% endcache %}
序列化
关于Django中的序列化主要应用在将数据库中检索的数据返回给客户端用户,特别的Ajax请求一般返回的为Json格式。
1、serializers
from django.core import serializers ret = models.BookType.objects.all() data = serializers.serialize("json", ret)
2、json.dumps
import json #ret = models.BookType.objects.all().values('caption') ret = models.BookType.objects.all().values_list('caption') ret=list(ret) result = json.dumps(ret)
由于json.dumps时无法处理datetime日期,所以可以通过自定义处理器来做扩展,如:
import json from datetime import date from datetime import datetime class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field, datetime): return o.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field, date): return o.strftime('%Y-%m-%d') else: return json.JSONEncoder.default(self, field) # ds = json.dumps(d, cls=JsonCustomEncoder)
十二,信号
Django中提供了“信号调度”,用于在框架执行操作时解耦。通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者。
1、Django内置信号
Model signals pre_init # django的modal执行其构造方法前,自动触发 post_init # django的modal执行其构造方法后,自动触发 pre_save # django的modal对象保存前,自动触发 post_save # django的modal对象保存后,自动触发 pre_delete # django的modal对象删除前,自动触发 post_delete # django的modal对象删除后,自动触发 m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发 class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发 Management signals pre_migrate # 执行migrate命令前,自动触发 post_migrate # 执行migrate命令后,自动触发 Request/response signals request_started # 请求到来前,自动触发 request_finished # 请求结束后,自动触发 got_request_exception # 请求异常后,自动触发 Test signals setting_changed # 使用test测试修改配置文件时,自动触发 template_rendered # 使用test测试渲染模板时,自动触发 Database Wrappers connection_created # 创建数据库连接时,自动触发
对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数:
from django.core.signals import request_finished from django.core.signals import request_started from django.core.signals import got_request_exception from django.db.models.signals import class_prepared from django.db.models.signals import pre_init, post_init from django.db.models.signals import pre_save, post_save from django.db.models.signals import pre_delete, post_delete from django.db.models.signals import m2m_changed from django.db.models.signals import pre_migrate, post_migrate from django.test.signals import setting_changed from django.test.signals import template_rendered from django.db.backends.signals import connection_created def callback(sender, **kwargs): print("xxoo_callback") print(sender,kwargs) xxoo.connect(callback) # xxoo指上述导入的内容
from django.core.signals import request_finished from django.dispatch import receiver @receiver(request_finished) def my_callback(sender, **kwargs): print("Request finished!")
2、自定义信号
a. 定义信号
import django.dispatch pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
b. 注册信号
def callback(sender, **kwargs): print("callback") print(sender,kwargs) pizza_done.connect(callback)
c. 触发信号
from 路径 import pizza_done pizza_done.send(sender='seven',toppings=123, size=456)