2013-02-06 31 views
5

Tôi ghi đè lên các phương pháp tiết kiệm của một ModelForm và tôi không biết tại sao nó sẽ gây ra đệ quy:Python lớp trang trí mở rộng lớp gây đệ quy

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

Nguyên nhân này:

maximum recursion depth exceeded while calling a Python object 

stacktrace cho thấy dòng này lặp lại tự gọi mình:

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

Bây giờ, trang trí nguệch ngoạc là e này:

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

Như @DanielRoseman gợi ý rằng trang trí Mùi tây mở rộng AccountForm làm cho super(AccountForm,self) để giữ tự xưng là, giải pháp là gì?

Ngoài ra, tôi không thể hiểu được lý do tại sao điều này sẽ gây ra đệ quy.

+1

Tôi không nghĩ rằng bạn có nghĩa vụ phải trả lại phương thức save() cha mẹ, chỉ cần làm siêu (AccountForm, self) .save (* args, ** kwargs) – PepperoniPizza

+1

Bạn có chắc đó là mã thực sự của bạn? Điều này thường xảy ra nếu bạn đã nhầm lẫn gọi superclass trong cuộc gọi 'super' - ví dụ bạn thực sự có một lớp con của AccountForm, và trong phương thức save đã ghi đè đó bạn vẫn đang gọi' super (AccountForm ...) ' . –

+0

Cảm ơn @DanielRoseman Tôi đã cập nhật câu hỏi –

Trả lời

6

Những gì bạn có thể làm là chỉ cần gọi trực tiếp cho phương thức của cha mẹ:

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

Điều này nên tránh gọn gàng vấn đề được trình bày lớp của bạn giới thiệu. Một lựa chọn khác sẽ tự gọi trang trí trên một lớp cơ sở khác nhau được đặt tên, thay vì sử dụng @ cú pháp:

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

AccountForm = parsleyfy(AccountFormBase) 

Tuy nhiên, bạn cũng có thể muốn xem xét sử dụng một pre-save signal thay vào đó, tùy thuộc vào những gì bạn đang cố gắng để do - đó là cách người ta thường thêm chức năng sẽ xảy ra trước khi phần còn lại của quá trình lưu mô hình trong Django.


Đối với tại sao điều này xảy ra, hãy xem xét những gì sẽ xảy ra khi các mã được đánh giá.

Đầu tiên, một lớp học được khai báo. Chúng tôi sẽ tham khảo định nghĩa lớp gốc này là Foo để phân biệt với định nghĩa lớp sau mà trình trang trí sẽ tạo. Lớp này có phương thức save thực hiện cuộc gọi super(AccountForm, self).save(...).

Lớp này sau đó được chuyển đến trang trí, xác định lớp mới mà chúng tôi sẽ gọi Bar và kế thừa từ Foo. Do đó, Bar.save tương đương với Foo.save - nó cũng gọi số super(AccountForm, self).save(...). Lớp thứ hai này sau đó được trả về từ trang trí.

Lớp được trả lại (Bar) được gán cho tên AccountForm.

Vì vậy, khi bạn tạo đối tượng AccountForm, bạn đang tạo đối tượng thuộc loại Bar. Khi bạn gọi .save(...) trên đó, nó đi và tra cứu Bar.save, thực tế là Foo.save vì nó được kế thừa từ Foo và không bao giờ bị ghi đè.

Như chúng tôi đã lưu ý trước đây, Foo.save gọi super(AccountForm, self).save(...). Vấn đề là do trình trang trí lớp học, AccountForm không phải là Foo, đó là Bar - và Bar của cha mẹ là Foo.

Vì vậy, khi Foo.save tra cứu AccountForm 's parent, it get ... Foo. Điều này có nghĩa rằng khi nó cố gắng gọi .save(...) trên cha mẹ đó, nó thực sự chỉ gió lên gọi chính nó, do đó các đệ quy vô tận.

+0

Sự khác biệt giữa siêu (AccountForm, self) .ave vs forms.ModelForm.save? Tôi nghĩ rằng phương pháp lưu là phương pháp dụ và bạn không thể gọi nó như thế? –

+0

@JamesLin Bạn có thể gọi nó miễn là bạn tự cung cấp cho nó một thể hiện của lớp làm đối số đầu tiên. Đối với sự khác biệt là gì, hãy xem phần thứ hai của câu trả lời của tôi. – Amber

+1

@JamesLink Tôi cũng đã thêm một lưu ý về [tín hiệu lưu trước] (https://docs.djangoproject.com/en/dev/ref/signals/#pre-save), đó là cách ưa thích của Django để thêm chức năng kích hoạt ngay trước khi một mô hình tiết kiệm. – Amber

0

Dưới đây là những gì tôi đã thực hiện để làm cho nó hoạt động, tôi hoặc là có thể thay đổi lớp parsleyfy ghi đè lên các phương pháp tiết kiệm như thế này:

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

hoặc thay đổi phương thức tiết kiệm của AccountForm là như thế này:

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

Một điều tôi không khác biệt là gì, là super(Class, self) vs super(Parent, self) tôi đã yêu cầu này question

Các vấn đề liên quan