2010-02-22 23 views
6

Próbuję zrobić coś takiego jak these proposed signal decorators. Oprócz dekoratora, który łączy dekorowaną metodę z sygnałem (z nadawcą sygnału jako argumentem dla dekoratora), chciałbym użyć dekoratora na metodach klasowych.Sygnał Django za pośrednictwem dekoratora na podstawie metody modelowania?

Chciałbym użyć dekorator tak:

class ModelA(Model): 

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

Dekorator jest:

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 

błąd pojawia się, kiedy to zrobić:

 
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. 

Chyba problem polega na tym, że @classmethod zwraca obiekt metody klasy, który nie jest możliwy do wywołania. Nie bardzo rozumiem, jak działa pod maską, ale domyślam się, że obiekt metody klasy nie jest tłumaczony na wywoływany, dopóki nie zostanie uzyskany dostęp z klasy, na przykład ModelA.observe_model_b_saved. Czy jest jakiś sposób, żebym mógł jednoznacznie zdefiniować moją metodę jako metodę klasy lub instancji w modelu i (2) podłączyć ją do sygnału za pomocą dekoratora bezpośrednio w definicji metody? Dzięki!

+0

Czy to działa, jeśli zamienisz kolejność @klassmethod i @connect? – Wogan

+0

Nie dostaję teraz: "zauważ_model_b_saved() pobiera dokładnie 4 argumenty nie-słów kluczowych (0 podane)". Co to znaczy? –

Odpowiedz

2

To nie jest jasne z twojego przykładowego kodu, więc chciałbym zapytać, czy odbiornik sygnału rzeczywiście musi być @classmethod? To znaczy. Czy regularna metoda (a następnie użyć self.__class__, jeśli nadal potrzebujesz dostępu do samej klasy)? Czy musi to być w ogóle metoda (czy możesz po prostu użyć funkcji)?

Innym rozwiązaniem może być użycie drugiej metody, aby słuchać sygnału i przekazać połączenie do @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) 
+0

Dziękuję Myślę, że zamierzam pójść z rozwiązaniem, które ktoś zasugerował innej wersji tego pytania: dekoratorzy klas. Dekorator klasy może utworzyć połączenie sygnałowe po załadowaniu całej klasy, co eliminuje problem braku możliwości bezpośredniego dostępu do metody klasy. http://stackoverflow.com/questions/2366713/can-a-python-decorator-of-an-instance-method-access-the-class –

1

można zrobić to @staticmethod zamiast? W ten sposób możesz po prostu zamienić kolejność dekoratorów.

class ModelA(Model): 

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

Trzeba odwołać się do klasy o pełnej nazwie zamiast się zdały argumentu CLS, ale to pozwoli Ci zachować podobną organizację kodu.

+0

Dzięki, ale to rozwiązanie nie zadziała, ponieważ w metodach I zależą od dynamicznego dostępu do klasy w celu pobrania elementów, które mogą być przeciążone. Na przykład. superklasa definiuje metodę, która opiera się na pewnym atrybucie, ale wtedy podklasa przeciąża atrybut. Potrzebuję przeciążonego atrybutu, a nie tego, który pochodzi od jawnego nazwania klasy imieniem i nazwiskiem. –

0

Na podstawie odpowiedzi Matta zadziałała mi sztuczka @staticmethod. Możesz użyć ciągu, aby odwołać się do modelu niezwiązanego z konkretem.

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 
Powiązane problemy