教你如何在Django 1.6中正确使用 Signal
简单回答是:在其他方法无法使用的情况下,才最后考虑使用signal.
因为新的django开发人员得知signal之后,往往会很高兴去使用它.他们在能使用signal的地方就使用signal,并且这是他们觉得自己是django专家一样.然而,像这样编码一段时间后,django项目就会变得异常复杂,许多内容都纠结在一起无法解开.
许多开发者也会将djangosignal和异步消息列队(例如celery)搞混.signal是同步处理,因此通过signal调用大处理量的进程时并无法提高性能.事实上,将这些需要大处理量的进程移到signal中被视作是一种不好的习惯.
1.何时使用signal
以下情况不要使用signal:
signal与一个model紧密相关,并能移到该model的save()时
signal能使用modelmanager代替时
signal与一个view紧密相关,并能移到该view中时
以下情况可以使用signal:
signal的receiver需要同时修改对多个model时
将多个app的相同signal引到同一receiver中处理时
在某一model保存之后将cache清除时
无法使用其他方法,但需要一个被调函数来处理某些问题时
2.Signal的代替方法
使用mod而来manager
以下代码演示了当用户创建Eventmodel时,需要通知管理员,如果改写model中的post_save(),则需要添加额外的逻辑来区分用户还是管理员:
#myapp/managers.py fromdjango.dbimportmodels classEventManager(models.Manager): defcreate_event(self,title,start,end,creator): event=self.model(title=title,start=start,end=end,creator=creator) event.save() event.notify_admins() returnevent
在model中设置modelmanager:
#myapp/models.py fromdjango.confimportsettings fromdjango.core.mailimportmail_admins fromdjango.dbimportmodels frommodel_utils.modelsimportTimeStampedModel from.managersimportEventManager classEvent(TimeStampedModel): STATUS_UNREVIEWED,STATUS_REVIEWED=(0,1) STATUS_CHOICES=( (STATUS_UNREVIEWED,"Unreviewed"), (STATUS_REVIEWED,"Reviewed") ) title=models.CharField(max_length=100) start=models.DateTimeField() end=model.dateTimeField() status=models.IntegerField(choices=STATUS_CHOICES,default=STATUS_UNREVIEWED) creator=models.ForeignField(settings.AUTH_USER_MODEL) objects=EventManager() defnotify_admins(self): subject="{user}submittedanewevent!".format(user=self.creator.get_full_name()) message="""TITLE:{title} START:{start} END:{end}""".format(title=self.title,start=self.start,end=self.end) mail_admins(subject=subject,message=message,fail_silently=False)
在view中使用create_event()代替create()时,便会通知管理员了.
在其他代码中验证model
如果你使用pre_savesignal来验证某一model,则应当尝试自己写一个validator取代之.如果验证是通过ModelForm时,通过改写clean()实现验证.
使用model的save()和delete()
如果使用pre_save或post_savesignal,如果可以,则将这些代码移到model的save()方法中.
同样如果使用pre_delete或post_deletesignal,如果可以,则将这些代码移到model的delte()方法中.
使用其他代码代替signal
如果可能,我们可以将signal的逻辑使用其他帮助程序实现.