https://docs.djangoproject.com/en/1.6/topics/signals/ -- django官网关于django signal的介绍
1、使用django signal
django signal类定义在django/dispatch/dispatch.py中
django 包含一个称为 signal dispatcher, 使得框架内其他松耦合或不相关的应用在某些特定的事件发生后得到通知, 即在特定事件发生时, 使用signal 能够通知指定的接收者。这在多个代码片段同时关注同一特定事件时显得尤为有用。
post_syncdb signal
post_syncdb是在应用安装后 syncdb命令或者flush命令触发的,django-admin.py flush 命令用于将数据库还原为初次运行 syncdb命令后的状态。
以下是转帖别人的:
在web开发中, 你可能会遇到下面这种场景:
在用户完成某个操作后, 自动去执行一些后续的操作. 譬如用户完成修改密码后,
你要发送一份确认邮件.
当然你可以把逻辑写在一起, 但是有个问题是, 通常前置动作(触发操作)会不止一种(如用户更改了其它信息的确认邮件), 这时候这个逻辑会需要写多次, 所以你可能会想着DRY, 于是你把它写到了一个函数中,每次调用. 当然这是没问题的.
但是, 如果你换个思路你会发现另一个完全不同的方案, 即:
类似于daemon的程序监听着特定的事件
前置操作来触发相应的事件
监听程序执行对应的操作
这样的好处是什么呢?
在各种高级语言中都会有类似的特性,如java, javascript等.而在 django 中我们使用 signal.
本文会着重介绍 django 的 signal 相关知识.
django的signal定义
什么是signal?
django 包含一个称为 signal dispatcher, 用来解耦合框架中发生的事件与 得到通知的应用 之间的逻辑. 简单地说, 在特定事件发生时, 使用signal 发送者 能够通知一系列 接收者(一个或者多个).
python 本身没有类似机制的支持, 但是 django 基于 PyDispatcher 进行了性能 和代码结构上的优化来增加了signal功能.
如何使用django的signal
在使用signal之前,我们先了解下django signal的处理流程.
参考下图:
那么我们来逐步完成我们简单的signal.
场景
我们有2个页面 ,一个是文章显示页面,一个是文章增加页面, 文章只有title,content及is_public三个域.
我们使用signal是完成,当用户添加一个文章后,我们置其is_public=False, 我们使用signal来实现此功能.
注册signal
下面是测试用的project的文件结构,具体可以 下载源代码 来在本地运行.
.
|-- __init__.py
|-- __init__.pyc
|-- logs
| `-- filelog.log
|-- manage.py
|-- settings.py
|-- settings.pyc
|-- signal.db
|-- testsignal
| |-- __init__.py
| |-- __init__.pyc
| |-- models.py
| |-- models.pyc
| |-- signals.py
| |-- signals.pyc
| |-- templates
| | |-- add.html
| | |-- base.html
| | `-- index.html
| |-- tests.py
| |-- urls.py
| |-- urls.pyc
| |-- views.py
| `-- views.pyc
|-- urls.py
`-- urls.pyc
testsignal是我们建立的示例app, 我们首先要注册signal, 具体代码如下(testsignal/signals.py):
import django.dispatchdelete_done = django.dispatch.Signal(providing_args=['obj'])
引入django的包, 并且注册 delete_done为我们将要使用的signal.
关联signal对应的listener
然后我们关联对应的listener, 代码如下: (testsignal/models.py)
from django.db.models.signals import pre_save
from django.db import models
import logging
import signals
# Create your models here.
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
is_public = models.BooleanField(default=True, blank=True)
def delete(self):
#self.is_public = False
signals.delete_done.send(sender=Article, obj=self)
def __unicode__(self):
return self.title
def zhutao(sender, kwargs):
logging.debug(kwargs)
if "obj" in kwargs:
obj = kwargs.get("obj")
logging.debug(obj.is_public)
obj.is_public = False
obj.save()
logging.debug("signal recieved! zhutao is called.")
logging.debug(obj.is_public)
signals.delete_done.connect(zhutao, sender=Article)
我们来看最后一行, signals.delete_done.connect(zhutao, sender=Article), 即将 我们上面注册的 delete_done 和 监听函数 zhutao 关联了起来, 而 这里的 sender=Article, 则是用来限制只有当Article有更新时我们才会触发这个事件.
触发监听事件
最后,我们要在特定操作发生时触发相应的监听函数, 具体代码如下(testsignal/views.py):
from django.http import HttpResponse,HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.shortcuts import render_to_response
import signals
from models import *
def index(request):
articles = Article.objects.all().order_by("-id")
return render_to_response("index.html", {"articles":articles})
def add(request):
if request.method == "POST":
title = request.POST.get("title", "")
content = request.POST.get("content", "")
if title and content:
article = Article(title=title, content=content)
article.save()
article.delete()
return HttpResponseRedirect(reverse(index))
return render_to_response("add.html", {})
其它的只是django的普通views.py中的方法,我们主要来看 article.delete() 这行代码, 执行这个后,我们会调用models.py中的delete方法,见上面的models.py中的代码, 它会执行下面一行代码:
signals.delete_done.send(sender=Article, obj=self)
面这行代码正是向delete_done发送了事件的触发,此时,对应的监听函数 zhutao 会得到执行,
从而将 is_public 置为False.
我们的任务也得以完成.
具体的说明可以 下载源代码 来运行,你会在logs/filelog.log文件中看到对应的log输出.
示例输出为:
2009-10-27 03:28:27,202 DEBUG models.zhutao Line:26 {'signal': <django.dispatch.dispatcher.Signal object at 0x9523e4c>, 'obj': <Article: aaa>}
2009-10-27 03:28:27,203 DEBUG models.zhutao Line:29 True
2009-10-27 03:28:27,209 DEBUG models.zhutao Line:32 signal recieved! zhutao is called.
2009-10-27 03:28:27,209 DEBUG models.zhutao Line:33 False
一些其它的应用
我们上面提到过 django, pinax 也大量地使用了signal技术,那我们下面简要看看, django 中有哪些具体的应用.
在 django 中, 主要有下面几类:
Model signals
pre_init
post_init
pre_save
post_save
pre_delete
post_delete
class_prepared
Management signals
post_syncdb
Request/response signals
request_started
request_finished
got_request_exception
Test signals
template_rendered
这些都是 django 内置的signals来方便用户来进行一些特定的操作,具体的介绍可以参考: built-in signals.
至于 pinax 可以下载其源代码来具体查看.
可能的应用场景
那么signals有哪些可能的应用场景呢?我们能够在什么样的场景下使用呢?
从定义来看,其实已经很明确了, 如果一个操作可能会触发多个后续事件, 此时使用signals会非常方便.
那么具体的应用可以考虑:
sns中的事件通知, 如用户发表了一篇博文, 然后通知所有的好友
用户信息的更改的邮件通知
用户订制信息的邮件通知等
结论
django 的signals是非常强大的, 如果我们能够很好地使用,则能够大大地提高代码的可维护性, 复用性,以及减少耦合等.