Django信号在模型方法上使用装饰器?

9 投票
3 回答
6186 浏览
提问于 2025-04-15 19:34

我正在尝试做一些类似于这些提议的信号装饰器的事情。除了有一个装饰器可以将被装饰的方法连接到一个信号(装饰器的参数是信号的发送者)之外,我还想在类的方法上使用这个装饰器。

我想这样使用这个装饰器:

class ModelA(Model):

    @connect.post_save(ModelB)
    @classmethod
    def observe_model_b_saved(cls, sender, instance, created, **kwargs):
        # do some stuff
        pass

这个装饰器是:

from django.db.models import signals
def post_save(sender):
    def decorator(view):
        signals.post_save.connect(sender=sender, receiver=view)
        return view
    return decorator

当我这样做时出现的错误是:

File "/Library/Python/2.6/site-packages//lib/python2.6/site-packages/django/dispatch/dispatcher.py", line 78, in connect
AssertionError: Signal receivers must be callable.

我想问题出在@classmethod上,它返回一个类方法对象,而这个对象是不可调用的。我其实不太明白classmethod是怎么工作的,但我从这个参考页面中推测,类方法对象在被类访问之前是不会变成可调用的,比如说ModelA.observe_model_b_saved。有没有办法让我同时做到(1)在模型上将我的方法定义为类方法或实例方法,以及(2)直接在方法定义上使用装饰器将其连接到信号?谢谢!

3 个回答

2

根据Matt的回答,使用@staticmethod这个技巧对我有效。你可以用一个字符串来间接引用一个模型。

class Foo(Model):

    @staticmethod
    @receiver(models.signals.post_save, sender='someappname.Foo')
    def post_save(sender, instance, created, **kwargs):
            print 'IN POST SAVE', sender, instance.id, created
2

从你的示例代码来看,情况不是很清楚。我想问一下,信号监听器真的必须是一个 @classmethod 吗?也就是说,普通的方法可以吗?如果可以的话,你可以用 self.__class__ 来访问类本身吗?这个监听器真的需要是一个方法吗?能不能直接用一个函数呢?

另外一个选择是,可以用第二个方法来监听信号,然后把调用委托给 @classmethod

class ModelA(Model): 

    @classmethod 
    def do_observe_model_b_saved(cls, sender, instance, created, **kwargs): 
        # do some stuff 
        pass 

    @connect.post_save(ModelB) 
    def observe_model_b_saved(self, sender, instance, created, **kwargs): 
        self.do_observe_model_b_saved(sender, instance, created, **kwargs)
3

你能把它改成一个 @staticmethod 吗?这样的话,你只需要调换一下装饰器的顺序就可以了。

class ModelA(Model):

    @staticmethod
    @connect.post_save(ModelB)
    def observe_model_b_saved(sender, instance, created, **kwargs):
        # do some stuff
        pass

你需要用类的全名来引用它,而不是像之前那样通过 cls 参数传递过来,但这样可以让你的代码结构保持相似。

撰写回答