I'm overwriting the save method of a ModelForm
and I don't know why it would cause recursion:
我正在覆盖ModelForm的save方法,我不知道它为什么会导致递归:
@parsleyfy
class AccountForm(forms.ModelForm):
def save(self, *args, **kwargs):
# some other code...
return super(AccountForm, self).save(*args,**kwargs)
Causes this:
maximum recursion depth exceeded while calling a Python object
Stacktrace shows this line repetitively calling itself:
Stacktrace显示此行反复调用自身:
return super(AccountForm, self).save(*args,**kwargs)
Now, the parsley decorator is like this:
现在,欧芹装饰器是这样的:
def parsleyfy(klass):
class ParsleyClass(klass):
# some code here to add more stuff to the class
return ParsleyClass
As @DanielRoseman suggested that the Parsley decorator extending the AccountForm
causes the super(AccountForm,self)
to keep calling itself, what's the solution?
正如@DanielRoseman所说,扩展AccountForm的Parsley装饰器导致super(AccountForm,self)继续调用自己,解决方案是什么?
Also I cannot get my head around this why this would cause recursion.
此外,我无法理解为什么这会导致递归。
2 个解决方案
#1
6
What you could do is just call the parent's method directly:
你能做的就是直接调用父方法:
@parsleyfy
class AccountForm(forms.ModelForm):
def save(self, *args, **kwargs):
# some other code...
return forms.ModelForm.save(self, *args,**kwargs)
This should neatly avoid the issue introduced by your class decorator. Another option would be to manually call the decorator on a differently named base class, rather than using @
syntax:
这应该整齐地避免您的类装饰器引入的问题。另一种选择是在不同名称的基类上手动调用装饰器,而不是使用@语法:
class AccountFormBase(forms.ModelForm):
def save(self, *args, **kwargs):
# some other code...
return super(AccountFormBase, self).save(*args,**kwargs)
AccountForm = parsleyfy(AccountFormBase)
However, you might also want to consider using a pre-save signal instead, depending on what you're trying to do - it's how one normally adds functionality that should happen before the rest of the model save process in Django.
但是,您可能还需要考虑使用预保存信号,具体取决于您尝试执行的操作 - 这是通常添加在Django中的其余模型保存过程之前应该发生的功能的方式。
As for why this is occurring, consider what happens when the code is evaluated.
至于为什么会发生这种情况,请考虑在评估代码时会发生什么。
First, a class is declared. We'll refer to this original class definition as Foo
to distinguish it from the later class definition that the decorator will create. This class has a save
method which makes a super(AccountForm, self).save(...)
call.
首先,声明一个类。我们将这个原始的类定义称为Foo,以区别于装饰器将创建的后一个类定义。这个类有一个save方法,它可以调用super(AccountForm,self).save(...)。
This class is then passed to the decorator, which defines a new class which we'll call Bar
, and inherits from Foo
. Thus, Bar.save
is equivalent to Foo.save
- it also calls super(AccountForm, self).save(...)
. This second class is then returned from the decorator.
然后将该类传递给装饰器,装饰器定义一个我们称之为Bar的新类,并继承自Foo。因此,Bar.save相当于Foo.save - 它也称为super(AccountForm,self).save(...)。然后从装饰器返回第二个类。
The returned class (Bar
) is assigned to the name AccountForm
.
返回的类(Bar)被分配给名称AccountForm。
So when you create an AccountForm
object, you're creating an object of type Bar
. When you call .save(...)
on it, it goes and looks up Bar.save
, which is actually Foo.save
because it inherited from Foo
and was never overridden.
因此,当您创建AccountForm对象时,您将创建一个Bar类型的对象。当你在它上面调用.save(...)时,它会查找Bar.save,它实际上是Foo.save,因为它继承自Foo并且从未被覆盖过。
As we noted before, Foo.save
calls super(AccountForm, self).save(...)
. The problem is that because of the class decorator, AccountForm
isn't Foo
, it's Bar
- and Bar
's parent is Foo
.
正如我们之前提到的,Foo.save调用super(AccountForm,self).save(...)。问题是因为类装饰器,AccountForm不是Foo,它是Bar - 而Bar的父级是Foo。
So when Foo.save
looks up AccountForm
's parent, it gets... Foo
. This means that when it tries to call .save(...)
on that parent, it actually just winds up calling itself, hence the endless recursion.
因此,当Foo.save查找AccountForm的父级时,它会得到...... Foo。这意味着当它试图在该父节点上调用.save(...)时,它实际上只是自动调用,因此无限递归。
#2
0
Here is what I have done to make it work, I could either change parsleyfy class to overwrite the save method like this:
以下是我为使其工作所做的工作,我可以更改parsleyfy类来覆盖save方法,如下所示:
def parsleyfy(klass):
class ParsleyClass(klass):
def save(self, *args, **kwargs):
return super(klass, self).save(*args, **kwargs)
return ParsleyClass
or change the AccountForm's save method to be like this:
或者将AccountForm的save方法更改为:
@parsleyfy
class AccountForm(forms.ModelForm):
def save(self, *args, **kwargs):
return super(forms.ModelForm, self).save(*args,**kwargs)
One thing I don't what the difference is, is super(Class, self)
vs super(Parent, self)
I have asked this question
有一点我没有什么区别,是超级(Class,self)vs super(Parent,self)我问过这个问题
#1
6
What you could do is just call the parent's method directly:
你能做的就是直接调用父方法:
@parsleyfy
class AccountForm(forms.ModelForm):
def save(self, *args, **kwargs):
# some other code...
return forms.ModelForm.save(self, *args,**kwargs)
This should neatly avoid the issue introduced by your class decorator. Another option would be to manually call the decorator on a differently named base class, rather than using @
syntax:
这应该整齐地避免您的类装饰器引入的问题。另一种选择是在不同名称的基类上手动调用装饰器,而不是使用@语法:
class AccountFormBase(forms.ModelForm):
def save(self, *args, **kwargs):
# some other code...
return super(AccountFormBase, self).save(*args,**kwargs)
AccountForm = parsleyfy(AccountFormBase)
However, you might also want to consider using a pre-save signal instead, depending on what you're trying to do - it's how one normally adds functionality that should happen before the rest of the model save process in Django.
但是,您可能还需要考虑使用预保存信号,具体取决于您尝试执行的操作 - 这是通常添加在Django中的其余模型保存过程之前应该发生的功能的方式。
As for why this is occurring, consider what happens when the code is evaluated.
至于为什么会发生这种情况,请考虑在评估代码时会发生什么。
First, a class is declared. We'll refer to this original class definition as Foo
to distinguish it from the later class definition that the decorator will create. This class has a save
method which makes a super(AccountForm, self).save(...)
call.
首先,声明一个类。我们将这个原始的类定义称为Foo,以区别于装饰器将创建的后一个类定义。这个类有一个save方法,它可以调用super(AccountForm,self).save(...)。
This class is then passed to the decorator, which defines a new class which we'll call Bar
, and inherits from Foo
. Thus, Bar.save
is equivalent to Foo.save
- it also calls super(AccountForm, self).save(...)
. This second class is then returned from the decorator.
然后将该类传递给装饰器,装饰器定义一个我们称之为Bar的新类,并继承自Foo。因此,Bar.save相当于Foo.save - 它也称为super(AccountForm,self).save(...)。然后从装饰器返回第二个类。
The returned class (Bar
) is assigned to the name AccountForm
.
返回的类(Bar)被分配给名称AccountForm。
So when you create an AccountForm
object, you're creating an object of type Bar
. When you call .save(...)
on it, it goes and looks up Bar.save
, which is actually Foo.save
because it inherited from Foo
and was never overridden.
因此,当您创建AccountForm对象时,您将创建一个Bar类型的对象。当你在它上面调用.save(...)时,它会查找Bar.save,它实际上是Foo.save,因为它继承自Foo并且从未被覆盖过。
As we noted before, Foo.save
calls super(AccountForm, self).save(...)
. The problem is that because of the class decorator, AccountForm
isn't Foo
, it's Bar
- and Bar
's parent is Foo
.
正如我们之前提到的,Foo.save调用super(AccountForm,self).save(...)。问题是因为类装饰器,AccountForm不是Foo,它是Bar - 而Bar的父级是Foo。
So when Foo.save
looks up AccountForm
's parent, it gets... Foo
. This means that when it tries to call .save(...)
on that parent, it actually just winds up calling itself, hence the endless recursion.
因此,当Foo.save查找AccountForm的父级时,它会得到...... Foo。这意味着当它试图在该父节点上调用.save(...)时,它实际上只是自动调用,因此无限递归。
#2
0
Here is what I have done to make it work, I could either change parsleyfy class to overwrite the save method like this:
以下是我为使其工作所做的工作,我可以更改parsleyfy类来覆盖save方法,如下所示:
def parsleyfy(klass):
class ParsleyClass(klass):
def save(self, *args, **kwargs):
return super(klass, self).save(*args, **kwargs)
return ParsleyClass
or change the AccountForm's save method to be like this:
或者将AccountForm的save方法更改为:
@parsleyfy
class AccountForm(forms.ModelForm):
def save(self, *args, **kwargs):
return super(forms.ModelForm, self).save(*args,**kwargs)
One thing I don't what the difference is, is super(Class, self)
vs super(Parent, self)
I have asked this question
有一点我没有什么区别,是超级(Class,self)vs super(Parent,self)我问过这个问题