2009-05-18 91 views
41

我想在管理員更改表單中製作一個完整的內聯表單集。因此,在我當前的情況下,當我點擊發票表單(在管理員)時,內聯訂單表單爲空。我想阻止人們創建沒有關聯訂單的發票。Django中的內聯表單驗證

任何人都知道一個簡單的方法來做到這一點?

模型字段上的正常驗證(例如(required=True))在此實例中似乎不起作用。

回答

63

要做到這一點,最好的方法是定義一個自定義formset,並用一個乾淨的方法驗證至少存在一個發票順序。

class InvoiceOrderInlineFormset(forms.models.BaseInlineFormSet): 
    def clean(self): 
     # get forms that actually have valid data 
     count = 0 
     for form in self.forms: 
      try: 
       if form.cleaned_data: 
        count += 1 
      except AttributeError: 
       # annoyingly, if a subform is invalid Django explicity raises 
       # an AttributeError for cleaned_data 
       pass 
     if count < 1: 
      raise forms.ValidationError('You must have at least one order') 

class InvoiceOrderInline(admin.StackedInline): 
    formset = InvoiceOrderInlineFormset 


class InvoiceAdmin(admin.ModelAdmin): 
    inlines = [InvoiceOrderInline] 
+0

完美解決方案,謝謝 – user108791 2009-05-18 15:01:41

+3

我發現,如果刪除複選框被選中,這是可能的0訂單來驗證。查看我的答案,找到解決該問題的修訂課程。 – 2009-12-10 23:13:47

+0

非常感謝您爲此解決方案(和丹增強)。 作爲一個可能的提示給別人,我做了一個'類MandatoryInlineFormSet(BaseInlineFormSet)',然後從中派生了InvoiceAdminFormSet。在我的InvoiceAdminFormSet中,我有一個用於自定義驗證的clean()方法,但首先調用MandatoryInlineFromSet.clean()。 – Kurt 2010-06-03 15:19:45

18

丹尼爾的回答非常好,它爲我工作的一個項目,但後來我意識到,由於道路Django表單的工作,如果你正在使用can_delete,檢查刪除框,同時節省,這是可能的,以驗證W¯¯/o任何命令(在這種情況下)。

我花了一段時間試圖找出如何防止發生。第一種情況很簡單 - 不包括將在計數中被刪除的表格。第二種情況更棘手......如果全部刪除框被選中,則clean未被調用。

不幸的是,代碼並不完全直截了當。從full_clean調用clean方法,當訪問error屬性時調用該方法。刪除子窗體時不訪問此屬性,因此永遠不會調用full_clean。我不是Django的專家,所以這可能是一個可怕的方式,但它似乎工作。

下面是修改後的類:

class InvoiceOrderInlineFormset(forms.models.BaseInlineFormSet): 
    def is_valid(self): 
     return super(InvoiceOrderInlineFormset, self).is_valid() and \ 
        not any([bool(e) for e in self.errors]) 

    def clean(self): 
     # get forms that actually have valid data 
     count = 0 
     for form in self.forms: 
      try: 
       if form.cleaned_data and not form.cleaned_data.get('DELETE', False): 
        count += 1 
      except AttributeError: 
       # annoyingly, if a subform is invalid Django explicity raises 
       # an AttributeError for cleaned_data 
       pass 
     if count < 1: 
      raise forms.ValidationError('You must have at least one order') 
2
class MandatoryInlineFormSet(BaseInlineFormSet): 

    def is_valid(self): 
     return super(MandatoryInlineFormSet, self).is_valid() and \ 
        not any([bool(e) for e in self.errors]) 
    def clean(self):   
     # get forms that actually have valid data 
     count = 0 
     for form in self.forms: 
      try: 
       if form.cleaned_data and not form.cleaned_data.get('DELETE', False): 
        count += 1 
      except AttributeError: 
       # annoyingly, if a subform is invalid Django explicity raises 
       # an AttributeError for cleaned_data 
       pass 
     if count < 1: 
      raise forms.ValidationError('You must have at least one of these.') 

class MandatoryTabularInline(admin.TabularInline): 
    formset = MandatoryInlineFormSet 

class MandatoryStackedInline(admin.StackedInline): 
    formset = MandatoryInlineFormSet 

class CommentInlineFormSet(MandatoryInlineFormSet): 

    def clean_rating(self,form): 
     """ 
     rating must be 0..5 by .5 increments 
     """ 
     rating = float(form.cleaned_data['rating']) 
     if rating < 0 or rating > 5: 
      raise ValidationError("rating must be between 0-5") 

     if (rating/0.5) != int(rating/0.5): 
      raise ValidationError("rating must have .0 or .5 decimal") 

    def clean(self): 

     super(CommentInlineFormSet, self).clean() 

     for form in self.forms: 
      self.clean_rating(form) 


class CommentInline(MandatoryTabularInline): 
    formset = CommentInlineFormSet 
    model = Comment 
    extra = 1 
4

@Daniel羅斯曼的解決方案是好的,但我有一些較少的代碼做一些修改,以做到這一點相同。

​​

試試這個它也可以:)