2013-02-06 10 views
5

mam nadpisywania ustawiony sposób na ModelForm i nie wiem, dlaczego to spowodować rekurencji:Python klasa dekorator powoduje rozszerzenie klasy rekurencji

@parsleyfy 
class AccountForm(forms.ModelForm): 
    def save(self, *args, **kwargs): 
     # some other code... 
     return super(AccountForm, self).save(*args,**kwargs) 

Powoduje to:

maximum recursion depth exceeded while calling a Python object 

StackTrace pokazuje linię powtarzalnie nazywającą się:

return super(AccountForm, self).save(*args,**kwargs) 

Teraz dekoratorem pietruszki jest lik e to:

def parsleyfy(klass): 
    class ParsleyClass(klass): 
     # some code here to add more stuff to the class 
    return ParsleyClass 

Jak @DanielRoseman zasugerował, że dekorator Pietruszka przedłużające AccountForm powoduje super(AccountForm,self) zachować nazywając się, co to jest rozwiązanie?

Również nie mogę zrozumieć, dlaczego to spowodowałoby rekursję.

+1

Nie sądzę, że powinieneś zwrócić metodę parent save(), po prostu wykonaj super (AccountForm, self) .save (* args, ** kwargs) – PepperoniPizza

+1

Czy jesteś pewien, że to twój rzeczywisty kod? Zwykle dzieje się tak, gdy przez pomyłkę odnosisz się do superklasy w wywołaniu "super" - np. Masz podklasę AccountForm, a w tej przesłoniętej metodzie zapisu wciąż wywołujesz 'super (AccountForm ...)' . –

+0

Dzięki @DanielRoseman Mam zaktualizowane pytanie –

Odpowiedz

6

Co można zrobić, to po prostu wywołać metodę rodzica bezpośrednio:

@parsleyfy 
class AccountForm(forms.ModelForm): 
    def save(self, *args, **kwargs): 
     # some other code... 
     return forms.ModelForm.save(self, *args,**kwargs) 

Należy starannie unikać problemu wprowadzonego przez klasy dekoratora. Innym rozwiązaniem byłoby ręcznie wywołać dekorator na różnie nazwie klasy bazowej, zamiast @ składnię:

class AccountFormBase(forms.ModelForm): 
    def save(self, *args, **kwargs): 
     # some other code... 
     return super(AccountFormBase, self).save(*args,**kwargs) 

AccountForm = parsleyfy(AccountFormBase) 

Jednakże, można także rozważyć przy użyciu pre-save signal zamiast, w zależności od tego, co starasz się do - tak zwykle dodaje się funkcje, które powinny się zdarzyć przed resztą procesu zapisywania modelu w Django.


chodzi o dlaczego ten występuje, należy rozważyć, co się dzieje, gdy kod jest oceniany.

Najpierw deklarowana jest klasa. Odwołujemy się do tej oryginalnej definicji klasy jako Foo, aby odróżnić ją od późniejszej definicji klasy, którą stworzy dekorator. Ta klasa ma metodę save, która wykonuje wywołanie super(AccountForm, self).save(...).

Ta klasa jest następnie przekazywana do dekoratora, który definiuje nową klasę, którą nazwiemy Bar, i dziedziczy po Foo. Zatem Bar.save jest odpowiednikiem Foo.save - to również wywołuje super(AccountForm, self).save(...). Ta druga klasa jest następnie zwracana przez dekoratora.

Zwrócona klasa (Bar) jest przypisana do nazwy AccountForm.

Kiedy tworzysz obiekt AccountForm, tworzysz obiekt typu Bar. Kiedy zadzwonisz na numer .save(...), pokaże się Bar.save, który jest w rzeczywistości Foo.save, ponieważ odziedziczył po Foo i nigdy nie został nadpisany.

Jak wcześniej wspomniano, Foo.save dzwoni super(AccountForm, self).save(...). Problem polega na tym, że ze względu na dekorator klas, AccountForm nie jest Foo, jego rodzicem jest Bar - i Bar jest to Foo.

Więc kiedy Foo.save patrzy rodzica AccountForm „s, robi ... Foo. Oznacza to, że gdy próbuje wywołać .save(...) na tym rodzica, to po prostu nakręca się, nazywając siebie, stąd niekończąca się rekursja.

+0

Jaka jest różnica między super (AccountForm, self) .save vs forms.ModelForm.save? Myślałem, że metoda zapisu jest metodą instancji i nie można tego tak nazwać? –

+0

@JamesLin Możesz wywoływać tak długo, jak ręcznie podajesz instancję klasy jako pierwszy argument. Jeśli chodzi o różnicę, zobacz drugą połowę odpowiedzi. – Amber

+1

@JamesLink Dodałem także notatkę o [sygnałach pre-save] (https://docs.djangoproject.com/en/dev/ref/signals/#pre-save), które są preferowanym sposobem dodawania funkcjonalności Django bezpośrednio przed zapisaniem modelu. – Amber

0

Oto co mam zrobić, aby to działało, mogę albo zmienić parsleyfy klasę nadpisać zapisać metodę tak:

def parsleyfy(klass): 
    class ParsleyClass(klass): 
     def save(self, *args, **kwargs): 
      return super(klass, self).save(*args, **kwargs) 
    return ParsleyClass 

lub zmienić zapisać metodę AccountForm by być tak:

@parsleyfy 
class AccountForm(forms.ModelForm): 
    def save(self, *args, **kwargs): 
     return super(forms.ModelForm, self).save(*args,**kwargs) 

Jedno nie jaka jest różnica, to super(Class, self) vs super(Parent, self) Poprosiłem ten question