Python自动化运维 - Django(五)中间件 - 缓存 - 信号

1 中间件

由一个需求引入中间件:我要记录所有url的访问日志,该如何操作?鉴于我们前面所学的知识,那么最好的方法就是使用装饰器了,那么如果我有1000个函数,就需要写1000遍装饰器... ... 。 这该咋办?利用django中间件完成!

django 中的中间件(middleware,在其他语言中会称为管道,或者 HTTP handler),在django中,中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。

引入了中间件之后,一个请求的生命周期就有了如下的改变

包含中间件的请求周期

一创建Python项目就会存在的中间件有:(存放在settings.py文件中)

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',
]

包含了中间件的请求周期如图所示:

注意:中间件是有顺序的,即从上倒下依次执行。从图上看,我们知道,请求的入方向和出方向都需要经过中间件,所以一个中间件应该有两个函数来对应不同的方向,这两个函数就是process_request(入)和process_response(出)。

process_request和process_response函数

这里以其他中间件为例子:

 1 class SecurityMiddleware(MiddlewareMixin):
 2     def __init__(self, get_response=None):
 3         self.sts_seconds = settings.SECURE_HSTS_SECONDS
 4         self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS
 5         self.sts_preload = settings.SECURE_HSTS_PRELOAD
 6         self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF
 7         self.xss_filter = settings.SECURE_BROWSER_XSS_FILTER
 8         self.redirect = settings.SECURE_SSL_REDIRECT
 9         self.redirect_host = settings.SECURE_SSL_HOST
10         self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT]
11         self.get_response = get_response
12 
13     def process_request(self, request):
14         path = request.path.lstrip("/")
15         if (self.redirect and not request.is_secure() and
16                 not any(pattern.search(path)
17                         for pattern in self.redirect_exempt)):
18             host = self.redirect_host or request.get_host()
19             return HttpResponsePermanentRedirect(
20                 "https://%s%s" % (host, request.get_full_path())
21             )
22 
23     def process_response(self, request, response):
24         if (self.sts_seconds and request.is_secure() and
25                 'strict-transport-security' not in response):
26             sts_header = "max-age=%s" % self.sts_seconds
27             if self.sts_include_subdomains:
28                 sts_header = sts_header + "; includeSubDomains"
29             if self.sts_preload:
30                 sts_header = sts_header + "; preload"
31             response["strict-transport-security"] = sts_header
32 
33         if self.content_type_nosniff and 'x-content-type-options' not in response:
34             response["x-content-type-options"] = "nosniff"
35 
36         if self.xss_filter and 'x-xss-protection' not in response:
37             response["x-xss-protection"] = "1; mode=block"
38 
39         return response
SecurityMiddleware例子

可以看到该中间件定义了process_request和process_response函数,其中process_request处理处理入方向的请求,process_response处理出方向的请求。

观察这两个函数可以发现:

  • process_request函数有一个参数request,用来表示用户请求的信息,并且在正常请求情况下函数并没有指定返回的内容,因为如果函数返回了非空或者非None的数据,请求将不会继续执行,而会直接交给process_response返回给用户
  • process_response函数存在两个参数,request和response,用来表示用户的请求信息,和应答信息(因为是位置参数,所以不要搞反了),函数最后返回了response,因为response存放的就是答复给用户的信息,你必须把信息返回给后续的中间件处理,并返回给用户才行,否则会直接报错。

注意:django 1.7-1.9 版本时,如果在某个一个中间件的process_request中返回了非空或者非None数据时,那么该请求将会从最后一个中间件的process_response函数开始进行返回,而django 1.10+ 版本时,修改了逻辑,会直接从当前中间件的process_response函数开始进行返回。

自定义中间件:(根据初始化的中间件代码可知,需要继承MiddlewareMixin类,所以需要先导入)

#1.这里在app01下创建middleware.py文件用于存放自定义中间件

from django.utils.deprecation import MiddlewareMixin         # 引入MiddlewareMixin类
from django.shortcuts import render,HttpResponse,redirect

class hello(MiddlewareMixin):

    def process_request(self,request):                       # 定义入方向的检查规则

        return HttpResponse('滚')                            # 入方向直接返回信息,那么请求将不会到达urls.py,会直接进行返回



#2. 在settings.py中添加我们配置的中间件

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',
    'app01.middleware.hello',                                # 因为我们没有建立规范化目录,所以这里从站点目录开始标示中间件位置
]
 1 from django.utils.deprecation import MiddlewareMixin
 2 from django.shortcuts import render,HttpResponse,redirect
 3 
 4 class hello(MiddlewareMixin):
 5 
 6     def process_request(self,request):
 7 
 8         pass
 9 
10     def process_response(self,request,response):
11 
12 
13         print(response)  #  <HttpResponse status_code=200, "text/html; charset=utf-8"> ,HttpResponse类,封装了应答信息
14 
15         return response
process_response实例

注意:

  1. 由于是从上倒下执行的,所以最后会执行我们的hello中间件
  2. 由于中间件的process_request返回了信息,那么所有请求将会永远返回('滚'),不信可以自己试试滚的感觉。
  3. 可以同时定义process_request和process_response函数,也可以只选择一个方向进行定义。

应用:

  • 给全站增加记录日志的功能(定义中间件,在process_request的时候调用log模块进行记录)
  • 当所有页面都要登录后才能查看的时候,就可以在中间件验证session或cookies。
 1 class Auth(MiddlewareMixin):     #添加认证中间件
 2 
 3     def process_request(self,request):
 4 
 5         print('m2.process_request')
 6 
 7         if request.path_info == '/test/':        # 对例外的目录进行放通,否则会进行死循环(如果不添加例外,那么所有请求都会被 重定向到/test/页面,而test页面也会检查session,所以会陷入死循环。 
 8 
 9             return None
10 
11         if not request.session.get('user',None):    # 所有的请求没有携带session,将会被返回
12 
13             return redirect('/test/')            # 跳转到test目录
14 
15     def process_response(self,request,response):
16 
17         print('m2.process_response')
18 
19         return response
利用中间件完成session验证

PS:很多人可能导入的时候报错,没有MiddlewareMixin这个类,那是因为由于MiddlewareMixin处于快要被淘汰的边缘,可能哪个版本就被取消了,具体原因就不知道了,所以为了不改变编写的方式,我们可以手动把如下源码放在文件的头部即可。

 1 class MiddlewareMixin(object):
 2     def __init__(self, get_response=None):
 3         self.get_response = get_response
 4         super(MiddlewareMixin, self).__init__()
 5 
 6     def __call__(self, request):
 7         response = None
 8         if hasattr(self, 'process_request'):
 9             response = self.process_request(request)
10         if not response:
11             response = self.get_response(request)
12         if hasattr(self, 'process_response'):
13             response = self.process_response(request, response)
14         return response
MiddlewareMixin中间件源码

PS:针对中间件的存放位置,建议有一个规范化的存放位置,比如在站点目录下创建middleware目录,然后在目录下创建我们的中间件文件,在文件中编写我们的中间件,然后在settings.py文件中进行导入。

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',
    'middleware.中间件文件名.中间件类名',
]

process_view和process_exception函数

引入process_view和process_exception函数之后,请求的顺序发生了变化

 从图上可以看到:

  1. 用户的请求经过所有中间件的process_request函数处理完毕后,由urls路由系统进行匹配,匹配完毕后会回到最初的中间件,继续按顺序执行它们的process_view函数,执行完毕后,会直接执行对应的views视图函数。
  2. 答复给用户的信息会有urls路由系统返回给最后一个中间件的process_exception函数,顺序执行后,再返回给最后一个中间件,继续执行process_reponse函数
  3. 需要注意的是,process_exception函数只要被任意一个中间件执行后,将不会执行后面中间件的process_exception函数。

 针对process_view函数:如果任意一个中间件的process_view函数返回了非空或者非None,那么后续将不会被执行,会直接倒序执行所有中间件的process_response函数

 1 from django.utils.deprecation import MiddlewareMixin
 2 from django.shortcuts import render,HttpResponse,redirect
 3 
 4 class hello(MiddlewareMixin):
 5 
 6     def process_view(self,request,callback,callback_args,callback_kwargs):
 7         '''
 8         :param request:             用户的请求
 9         :param callback:            匹配到的视图函数名
10         :param callback_args:       视图函数的位置参数
11         :param callback_kwargs:     视图函数的关键字参数
12         :return:                    如果return 非空非None会直接倒序执行所有middleware的process_response
13         '''
14         
15         pass
process_view函数
 1 from django.utils.deprecation import MiddlewareMixin
 2 from django.shortcuts import render,HttpResponse,redirect
 3 
 4 class hello(MiddlewareMixin):
 5 
 6     def process_exception(self,request,exceptions):
 7         '''
 8         :param request:         用户的请求
 9         :param exceptions:      异常信息
10         :return:                捕捉到异常后返回给用户的信息,也可以返回跳转连接
11         '''
12         
13         pass
process_exception函数

小结:

  如果在 view函数中 返回非空 非None。后续process_views不会执行,会倒序执行所有中间件的process_response,然后返回。

  如果视图函数报错,那么在 views函数执行完毕后,执行process_exception函数,最后执行process_response 函数,如果没错,就直接执行process_response进行返回。

  只要有中间件捕获异常并返回,那么就不会继续执行process_exceptions,会执行process_response进行返回。

应用:

  可以针对后台错误,返回维护页啊等等的。

process_template_response函数(知道就好)

  当我们views中的函数,返回的对象包含render方法时,这里就会执行。(HttpResoonse,redirect,render方法不会触发)

process_template_response(self,request,response)

# 需要返回response对象

  执行的顺序为:views函数执行完毕后,紧接着就会执行它。

不同版本django配置小差异

1、Django 1.9 和以前的版本:

MIDDLEWARE_CLASSES = (
'zqxt.middleware.BlockedIpMiddleware',
...其它的中间件
)

2、Django 1.10 版本 更名为 MIDDLEWARE(单复同形),写法也有变化。

MIDDLEWARE = (
'zqxt.middleware.BlockedIpMiddleware',
...其它的中间件
)

如果用 Django 1.10版本开发,部署时用 Django 1.9版本或更低版本,要特别小心此处。

2 Django缓存系统

  Django 是动态网站,一般来说需要实时地生成访问的网页,展示给访问者,这样,内容可以随时变化,但是从数据库读多次把所需要的数据取出来,要比从内存或者硬盘等一次读出来 付出的成本大很多。目前只有django框架自带缓存支持,只需要进行基本的配置就可以使用。

缓存系统工作原理

  对于给定的网址,尝试从缓存中找到网址,如果页面在缓存中,直接返回缓存的页面,如果缓存中没有,一系列操作(比如查数据库)后,保存生成的页面内容到缓存系统以供下一次使用,然后返回生成的页面内容。

配置django缓存

  django配置缓存非常简单,只需要在settings.py中添加如下配置即可完成:

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】) 
    }
}

django支持的缓存方式有:

  • 开发调试
  • 内存
  • 文件
  • 数据库
  • Memcache缓存(python-memcached模块)
  • Memcache缓存(pylibmc模块)

对应的引擎如下:

'django.core.cache.backends.db.DatabaseCache'                # 缓存到数据库中
'django.core.cache.backends.dummy.DummyCache'		     # 开发测试模式(假缓存) 哪都不放,相当于没有
'django.core.cache.backends.filebased.FileBasedCache'	     # 缓存到本地文件中
'django.core.cache.backends.locmem.LocMemCache'		     # 缓存到本地内存中
'django.core.cache.backends.memcached.MemcachedCache'	     # 缓存到memcached中去(利用python-memcached模块)*
'django.core.cache.backends.memcached.PyLibMCCache'          # 缓存到memcached中去(利用pylibmc模块)

注意:django默认不支持redis,需要使用第三方插件

PS:如果使用数据库引擎,那么配置完毕需要执行如下命令生成缓存信息表: python manage.py createcachetable

其他缓存参数

1)TIMEOUT         # cache 默认过期时间(seconds),未设置则为300s(5mins) 
2)OPTIONS         # 可选项配置,不同的后端,可选项配置不同
    MAX_ENTRIES     # 最大缓存个数(默认300)
    CULL_FREQUENCY  # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
3)KEY_PREFIX       # 默认会被自动加到所有缓存 keys 的前端 
4)VERSION         # 默认缓存 keys 的 version 
5)KEY_FUNCTION      # 生成最终缓存 keys 的函数路径
6)LOCATION             # 不同的引擎含义不同
        # 数据库时,这里填写表名
        # 存放到文件时,这里填写文件的路径及名称
        # 存放到内存,这里填写的是全局变量的名称
        # 放到memcached,这里填写的就是memcached的地址和端口

配置示例

1、放在文件中

 1 CACHES = {
 2     'default': {
 3         'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',# 引擎
 4         'LOCATION': '/Users/DahlHin/Downloads/cache',                  # 缓存的路径
 5         'TIMEOUT': 300,                                               # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
 6         'OPTIONS':{
 7             'MAX_ENTRIES': 300,                                       # 最大缓存个数(默认300)
 8             'CULL_FREQUENCY': 3,                                      # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
 9         },
10         # 'KEY_PREFIX': '',                                             # 缓存key的前缀(默认空)
11         # 'VERSION': 1,                                                 # 缓存key的版本(默认1)
12         # 'KEY_FUNCTION' 函数名                                          # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
13     }
14 }
View Code

2、放在memcache中

 1 CACHES = {
 2     'default': {
 3         'BACKEND':'django.core.cache.backends.memcached.MemcachedCache',
 4         'LOCATION': '192.168.10.3:11211',
 5         'OPTIONS': {
 6             'MAX_ENTRIES': '400',         # 使用memcached这会报错,提示没这个key,后续研究好了
 7         },
 8         'KEY_PREFIX': 'Django_cache',
 9         'VERSION': 1,
10     }
11 }
12 # 多台memcached可以设置权重,只需要修改location中的memcache list是元组即可
13 # ('172,16,0,1:11211',20),
14 # ('172,16,0,1:11211',30),
15 # 这些权重不是django来做的,是python-memcached模块来做的
16 
17 
18 
19 # memcached中的key value 如下:
20 stats cachedump 11 0
21 ITEM Django_cache:1:views.decorators.cache.cache_page..GET.e76ff9eca7dbf29254e8d11875f7f807.d41d8cd98f00b204e9800998ecf8427e.en-us.UTC [579 b; 1521963803 s]
22 END
View Code

应用缓存

  使用django缓存,包含三个级别:全局,视图函数,局部模板。

 1、对单独视图函数的缓存

from django.views.decorators.cache import cache_page   # 引入装饰器

@cache_page(3)     # 对test1视图函数进行缓存,括号里的表示时间单位是秒,这里的失效时间要优先于settings文件中配置的失效时间
def test1(request):
    import time
    ctime = time.time()
    return render(request,'test1.html',{'ctime':ctime})

def test2(request):
    import time
    ctime = time.time()
    return render(request,'test2.html',{'ctime':ctime})

2、局部模板

顾名思义,即在模板中的局部进行缓存

{% load cache %}        # 首先要加载缓存

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>TEST1 -> {{ ctime }}</h1>
    {% cache 10 'helloworld' %}          # 缓存开始,10表示时间,helloworld表示名称
    <h1>TEST1 -> {{ ctime }}</h1>        # 缓存的内容
    {% endcache %}       # 结束缓存
</body>
</html>

3、全局缓存 

  当针对全局来做某些的操作的时候,很容易的就想到了中间件,那么如何利用中间件来完成全局缓存呢?又应该在哪里进行缓存呢?

  什么时候查找缓存的?我们知道一个请求要经过各个中间件进行过滤才会到达urls路由系统,那么只有经过了层层的过滤才能访问数据(查找缓存),所以查找缓存的中间件,应该放在所有中间件的最后面。

  什么时候更新缓存呢?一个请求要经过所有中间件的response函数处理完毕后最后才会返回给用户,任何一个中间件的response都有可能更改数据,所以应该放在第一个中间件,让它最后返回给用户时,同时更新缓存系统。

如何使用全局缓存呢?django已经为我们写好了:

  • django.middleware.cache.UpdateCacheMiddleware 用于更新缓存,所以应该放在所有中间件的第一个位置
  • django.middleware.cache.FetchFromCacheMiddleware 用于查找缓存,所以应该放在所有中间件的最后一个位置

配置全局缓存:

MIDDLEWARE = [
    'django.middleware.cache.UpdateCacheMiddleware',      # 出方向
    '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',
    'django.middleware.cache.FetchFromCacheMiddleware',   # 入方向
]

  根据业务的缓存方向,可以知道:由于入向的 django.middleware.cache.FetchFromCacheMiddleware 只查找缓存,那么它应该只有process_request方法,而 出向的 django.middleware.cache.UpdateCacheMiddleware 只更新缓存,那么它应该只有process_response方法,这里可以自行查看源码验证。

配置全局缓存时,可选的其他参数(settings文件中):

CACHE_MIDDLEWARE_ALIAS = ""
CACHE_MIDDLEWARE_SECONDS = 10       # 全局缓存超时时间
CACHE_MIDDLEWARE_KEY_PREFIX = ""    # key的前缀

注意:这些参数仅仅用于控制全局缓存,如果没有设置,则按照cache定义的TIMOUT等参数定义的属性进行缓存。

PS:当三种类型同时使用时,那么优先级为:全局 优先于 视图 优先于 局部模版。

3 信号

  还是由一个需求开始:如果我要把所有数据库的操作记录日志,那么应该该怎么办?其实我们都知道,对数据库的操作,归根结底都是数据库的save操作,如果我能把所有的save操作捕获,那么我就能记录所有的save操作,那么信号就油然而生了。

  Django内部包含了一位“信号调度员”:当某事件在框架内发生时,它可以通知到我们的应用程序。 简而言之,当event(事件)发生时,signals(信号)允许若干 senders(寄件人)通知一组 receivers(接收者)。这在我们多个独立的应用代码对同一事件的发生都感兴趣时,特别有用。

  个人理解,django的signal可理解为django内部的钩子,当一个事件发生时,其他程序可对其作出相关反应,可通过signal来回调定义好的处理函数(receivers),从而更大程度的解耦我们的系统。

django提供的信号

django内部帮我们定义了多组信号:

Model signals
    pre_init                    # django的model执行其构造方法前,自动触发
    post_init                   # django的model执行其构造方法后,自动触发
    pre_save                    # django的model对象保存前,自动触发
    post_save                   # django的model对象保存后,自动触发
    pre_delete                  # django的model对象删除前,自动触发
    post_delete                 # django的model对象删除后,自动触发
    m2m_changed                 # django的model中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
    class_prepared              # 程序启动时,检测已注册的app中model类,对于每一个类,自动触发
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          # 创建数据库连接时,自动触发

使用信号

  要使用信号,就需要在我们的程序中预先注册信号,注册信号是要在程序一开始运行就需要执行的,通过什么办法来让程序已启动就加载呢?没错,我们知道在程序启动的时候,__init__文件就会被执行,所以可以在项目同名的目录的__init__文件中导入(写好py文件,在__init__中 import)。

# 1、内置信号,使用时需要先行导入
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


# 2、定义触发的函数
def callback(sender, **kwargs):
    print("xxoo_callback")
    print(sender,kwargs)

#    参数:
#   	sender:针对哪个类进行操作,触发信号的类
#    	kwargs:传递的其他参数,信号不同传递的参数不同。

# 3、注册我们自定义的函数,使得信号产生时,执行
XXX.connect(callback)        # 这里的XXX,表示上面的各种信号

  当某个信号被触发的时候,那么就会自动执行callback函数,我们可以利用该函数来记录日志,又或者在留言通知的场景下,当用户保存留言后,发送信号,对博主进行提示等。

  注意:django的信号是同步的,如果信号是post_save,那么每次保存都会去执行对应的函数,所以在大批量任务时,斟酌使用。

自定义信号

  虽然django给我们提供了很多信号,但是有时候我们需要对我们的业务进行定制化,举个例子:在监控的场景下,某些数据到达特定的阈值,我们就需要进行报警,那么我们就可以自定义一个报警的信号,用于处理报警后的动作,比如发短信、发微信等等。

使用自定义信号的三步:

  1. 创建信号
  2. 注册信号
  3. 触发信号

创建信号

  通过导入django提供的类来创建自定义信号:

import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

# pizza_done 为信号的名称
# providing_args 表示信号在出发时需要传递的两个参数,可自定义。

注册信号

  自定义信号使用,同样需要预先注册

# 定义触发函数
def callback(sender, **kwargs):
    print("callback")
    print(sender,kwargs)

# 注册信号
pizza_done.connect(callback)

触发信号 

 由于内置信号的触发者已经集成到Django中,所以其会自动调用,而对于自定义信号则需要开发者在任意位置触发。

# 在views中引入我们的信号
from 路径 import pizza_done

# 利用信号的send方法进行触发
pizza_done.send(sender='seven',toppings=123, size=456)
# 为什么要利用send,可以查看内置信号的源码

记录操作数据库的SQL

# utils/signales.py 创建并注册信号
import  django.dispatch

mysingal = django.dispatch.Signal(providing_args=['name','action'])

def loggin(sender,**kwargs):
    print(sender)      # 这里可以调用logging模块记录日志。
    print(kwargs)

mysingal.connect(loggin)


# views中触发信号
from utils import signales
from django.db import connection              # 用于查看数据库的操作语句
def add(request):
    abc=models.Test.objects.create(title='hello')
    sql = connection.queries
    signales.mysingal.send(sender=abc,name='daxin',action=sql)    # 主动触发信号,并传递参数
    return HttpResponse('ok')

  

 

posted @ 2017-09-17 21:25  SpeicalLife  阅读(506)  评论(0编辑  收藏  举报