簡單回答是: 在其他方法無法使用的情況下, 才最後考慮使用signal.
因為新的django開發人員得知signal之後, 往往會很高興去使用它. 他們在能使用signal的地方就使用signal, 並且這是他們覺得自己是django專家一樣. 然而, 像這樣編碼一段時間後, django項目就會變得異常複雜, 許多內容都糾結在一起無法解開.
許多開發人員也會將django signal和非同步訊息列隊(例如celery)搞混. signal是同步處理, 因此通過signal調用大處理量的進程時並無法提高效能. 事實上, 將這些需要大處理量的進程移到signal中被視作是一種不好的習慣.
1. 何時使用signal
以下情況不要使用signal:
signal與一個model緊密相關, 並能移到該model的save()時
signal能使用model manager代替時
signal與一個view緊密相關, 並能移到該view中時
以下情況可以使用signal:
signal的receiver需要同時修改對多個model時
將多個app的相同signal引到同一receiver中處理時
在某一model儲存之後將cache清除時
無法使用其他方法, 但需要一個被調函數來處理某些問題時
2. Signal的代替方法
使用mod而來manager
以下代碼示範了當使用者建立Event model時, 需要通知管理員, 如果改寫model中的post_save(), 則需要添加額外的邏輯來區分使用者還是管理員:
# myapp/managers.py from django.db import models class EventManager(models.Manager): def create_event(self, title, start, end, creator): event = self.model(title=title, start=start, end=end, creator=creator) event.save() event.notify_admins() return event
在model中設定model manager:
# myapp/models.py from django.conf import settings from django.core.mail import mail_admins from django.db import models from model_utils.models import TimeStampedModel from .managers import EventManager class Event(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() def notify_admins(self): subject = "{user} submitted a new event!".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_save signal來驗證某一model, 則應當嘗試自己寫一個validator取代之. 如果驗證是通過ModelForm時, 通過改寫clean()實現驗證.
使用model的save()和delete()
如果使用pre_save 或 post_save signal, 如果可以, 則將這些代碼移到model的save()方法中.
同樣如果使用pre_delete 或 post_delete signal, 如果可以, 則將這些代碼移到model的delte()方法中.
使用其他代碼代替signal
如果可能, 我們可以將signal的邏輯使用其他協助程式實現.