【问题标题】:Django Signal via Decorator on Model Method?通过模型方法上的装饰器发送 Django 信号?
【发布时间】:2011-01-19 14:54:15
【问题描述】:

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

我想像这样使用装饰器:

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 是如何在后台工作的,但我从this reference page 推测,类方法对象在从类访问之前不会被转换为可调用对象,例如ModelA.observe_model_b_saved。有什么方法可以(1)将我的方法定义为模型上的类或实例方法,以及(2)直接在方法定义上使用装饰器将其连接到信号?谢谢!

【问题讨论】:

  • 把@classmethod 和@connect 的顺序调换一下可以吗?
  • 不,现在我得到:“observe_model_b_saved() 正好采用 4 个非关键字参数(给定 0)”。这是什么意思?

标签: python django django-models decorator django-signals


【解决方案1】:

从您的示例代码中不清楚,所以我想问一下信号侦听器是否真的必须是@classmethod? IE。常规方法可以吗(如果您仍需要访问该类本身,请使用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)

【讨论】:

  • 谢谢,我想我会采用有人建议这个问题的另一个版本的解决方案:类装饰器。类装饰器可以在整个类加载完成后创建信号连接,这样就避免了无法直接访问类方法的问题。 stackoverflow.com/questions/2366713/…
【解决方案2】:

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

class ModelA(Model):

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

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

【讨论】:

  • 谢谢,但该解决方案对我不起作用,因为在方法中我依赖于动态访问类以检索可能重载的成员。例如。超类定义了一个依赖于某个属性的方法,但是子类重载了该属性。我需要重载属性,而不是通过名称明确命名类的属性。
【解决方案3】:

根据马特的回答,@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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-01
    • 2010-12-23
    • 1970-01-01
    • 1970-01-01
    • 2015-10-17
    • 2011-12-15
    • 2021-02-09
    • 1970-01-01
    相关资源
    最近更新 更多