Сигнал Django через декоратор по методу модели?

Я пытаюсь сделать что-то вроде этих предлагаемых декораторов сигналов. Помимо декоратора, который соединяет декорированный метод с сигналом (с отправителем сигнала в качестве аргумента для декоратора), я хотел бы использовать декоратор для методов класса.

Я бы хотел использовать декоратор так:

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) подключить его к сигналу, используя декоратор непосредственно в определении метода? Спасибо!


person Carl G    schedule 22.02.2010    source источник
comment
Это работает, если поменять местами @classmethod и @connect?   -  person Wogan    schedule 02.03.2010
comment
Нет, теперь я понимаю: Наблюдать_модель_b_saved () принимает ровно 4 аргумента, не являющихся ключевыми словами (задано 0). Что это означает?   -  person Carl G    schedule 02.03.2010


Ответы (3)


Это не ясно из вашего примера кода, поэтому я бы спросил, действительно ли слушатель сигнала должен быть @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)
person Tom    schedule 04.03.2010
comment
Спасибо, я думаю, что собираюсь пойти с решением, которое кто-то предложил для другой версии этого вопроса: декораторы классов. Декоратор класса может создать сигнальное соединение после завершения загрузки всего класса, и это устраняет проблему невозможности прямого доступа к методу класса. stackoverflow.com/questions/2366713/ - person Carl G; 22.03.2010

Не могли бы вы вместо этого сделать это @staticmethod? Таким образом, вы можете просто поменять местами декораторы.

class ModelA(Model):

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

Вам придется обращаться к классу по полному имени вместо того, чтобы передавать аргумент cls, но это позволит вам сохранить аналогичную организацию кода.

person Community    schedule 15.07.2010
comment
Спасибо, но это решение не сработает для меня, потому что в методах я зависим от динамического доступа к классу, чтобы получить элементы, которые могут быть перегружены. Например. суперкласс определяет метод, который полагается на некоторый атрибут, но затем подкласс перегружает атрибут. Мне нужен перегруженный атрибут, а не тот, который возникает в результате явного именования класса по имени. - person Carl G; 22.07.2010

Основываясь на ответе Мэтта, у меня сработал трюк @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
person jamesmfriedman    schedule 19.08.2016