I'm trying to do something like these proposed signal decorators. In addition to having a decorator that connects the decorated method to a signal (with the signal's sender as an argument to the decorator), I would like to use the decorator on class methods.
我正在尝试做这些提议的信号装饰器。除了有一个装饰器将装饰方法连接到一个信号(信号的发送者作为装饰器的参数),我想在类方法上使用装饰器。
I'd like to use the decorator like so:
我想像这样使用装饰器:
class ModelA(Model):
@connect.post_save(ModelB)
@classmethod
def observe_model_b_saved(cls, sender, instance, created, **kwargs):
# do some stuff
pass
The decorator is:
装饰者是:
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
The error I get when I do this is:
我这样做的错误是:
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.
I guess the problem is that @classmethod
returns a class method object which is not callable. I don't really understand how classmethod
works under the hood, but I surmise from this reference page that the class method object is not translated into a callable until it is accessed from the class, e.g., ModelA.observe_model_b_saved
. Is there any way that I can both (1) define my method as a class or instance method on a model, and (2) connect it to a signal using a decorator directly on the method definition? Thanks!
我想问题是@classmethod返回一个不可调用的类方法对象。我真的不明白classmethod是如何工作的,但我从这个参考页面推测,在从类中访问类方法对象之前,类方法对象不会被转换为可调用方法,例如ModelA.observe_model_b_saved。有什么方法可以兼顾(1)将我的方法定义为模型上的类或实例方法,以及(2)直接在方法定义上使用装饰器将其连接到信号?谢谢!
3 个解决方案
#1
2
It's not clear from your example code, so I'd be asking if the signal listener actually has to be a @classmethod
? I.e. Will a regular method do (and then use self.__class__
if you still need to access the class itself)? Does it need to be a method at all (can you just use a function)?
从你的示例代码中不清楚,所以我会问信号监听器是否真的必须是@classmethod?即一个常规方法会做(然后使用self .__ class__,如果你仍然需要访问类本身)?是否需要一个方法(你能使用一个函数)吗?
Another option might be to use a second method to listen to the signal and delegate the call to the @classmethod
:
另一种选择可能是使用第二种方法来监听信号并将调用委托给@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)
#2
1
Could you make it a @staticmethod instead? That way, you can just swap the order of the decorators.
你能把它改成@static方法吗?这样,您就可以交换装饰器的顺序。
class ModelA(Model):
@staticmethod
@connect.post_save(ModelB)
def observe_model_b_saved(sender, instance, created, **kwargs):
# do some stuff
pass
You'd have to refer to the class by full name instead of getting passed the cls argument, but this would allow you to keep a similar code organization.
您必须通过全名引用该类,而不是通过cls参数,但这将允许您保留类似的代码组织。
#3
0
Based off of Matt's answer, the @staticmethod trick worked for me. You can use a string to reference a model non-concretely.
基于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
#1
2
It's not clear from your example code, so I'd be asking if the signal listener actually has to be a @classmethod
? I.e. Will a regular method do (and then use self.__class__
if you still need to access the class itself)? Does it need to be a method at all (can you just use a function)?
从你的示例代码中不清楚,所以我会问信号监听器是否真的必须是@classmethod?即一个常规方法会做(然后使用self .__ class__,如果你仍然需要访问类本身)?是否需要一个方法(你能使用一个函数)吗?
Another option might be to use a second method to listen to the signal and delegate the call to the @classmethod
:
另一种选择可能是使用第二种方法来监听信号并将调用委托给@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)
#2
1
Could you make it a @staticmethod instead? That way, you can just swap the order of the decorators.
你能把它改成@static方法吗?这样,您就可以交换装饰器的顺序。
class ModelA(Model):
@staticmethod
@connect.post_save(ModelB)
def observe_model_b_saved(sender, instance, created, **kwargs):
# do some stuff
pass
You'd have to refer to the class by full name instead of getting passed the cls argument, but this would allow you to keep a similar code organization.
您必须通过全名引用该类,而不是通过cls参数,但这将允许您保留类似的代码组织。
#3
0
Based off of Matt's answer, the @staticmethod trick worked for me. You can use a string to reference a model non-concretely.
基于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