8

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

我想像这样使用装饰器:

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

我这样做时得到的错误是:

文件“/Library/Python/2.6/site-packages//lib/python2.6/site-packages/django/dispatch/dispatcher.py”,第 78 行,在连接中
AssertionError:信号接收器必须是可调用的。

我猜问题是@classmethod返回一个不可调用的类方法对象。我不太了解幕后classmethod的工作原理,但我从这个参考页面推测类方法对象在从类访问之前不会被转换为可调用对象,例如ModelA.observe_model_b_saved. 有什么方法可以(1)将我的方法定义为模型上的类或实例方法,以及(2)直接在方法定义上使用装饰器将其连接到信号?谢谢!

4

3 回答 3

3

你能把它改成@staticmethod吗?这样,您可以交换装饰器的顺序。

class ModelA(Model):

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

您必须通过全名引用该类,而不是通过 cls 参数,但这将允许您保持类似的代码组织。

于 2010-07-15T19:18:20.853 回答
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)
于 2010-03-04T03:30:32.687 回答
0

根据马特的回答,@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
于 2016-08-19T12:53:39.647 回答