2009-05-18 12 views
41

관리자 변경 양식 내에서 전체 인라인 formset을 만들고 싶습니다. 그래서 현재의 시나리오에서 인보이스 양식 (관리자)에 저장을 클릭하면 인라인 주문 양식이 비어 있습니다. 나는 명령이없는 송장을 만드는 사람들을 막고 싶습니다.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

완벽한 솔루션 덕분에 내가 삭제 확인란을 선택하는 경우, 그것은 0 명령으로 확인하는 것이 가능하다 발견 – user108791

+3

을 시도합니다. 그 문제를 해결하는 개정 된 클래스에 대한 내 대답을 참조하십시오. –

+0

이 픽스 (그리고 개선을 위해 Dan)에 감사드립니다. 다른 사람들에게 가능한 힌트로 '클래스 MandatoryInlineFormSet (BaseInlineFormSet)'을 만든 다음 InvoiceAdminFormSet을 파생 시켰습니다. 내 InvoiceAdminFormSet에는 사용자 정의 유효성 검사를 수행하지만 처음에는 MandatoryInlineFromSet.clean()을 다시 호출하는 clean() 메서드가 있습니다. – Kurt

18

다니엘의 대답은 우수하고 하나 개의 프로젝트에 나를 위해 일하지만, 나는이 방법에 장고 저장하는 동안 당신이 삭제 상자를 can_delete를 사용하여 확인하는 경우, 그것은 승의 유효성을 검사하는 것이 가능하다, 일을 형성 인해 실현/o 모든 명령 (이 경우).

나는 그런 일이 발생하지 않도록하는 방법을 찾아내는 동안 시간을 ​​보냈습니다. 첫 번째 상황은 쉽습니다. 계산서에서 삭제 될 양식은 포함시키지 마십시오. 두 번째 상황이 더 까다 롭습니다 ... 모두 삭제 상자가 선택된 경우 clean이 호출되지 않았습니다.

이 코드는 불행히도 간단하지 않습니다. clean 메서드는 error 속성에 액세스 할 때 호출되는 full_clean에서 호출됩니다. 이 속성은 하위 폼이 삭제 될 때 액세스되지 않으므로 full_clean은 호출되지 않습니다. 나는 장고 전문가가 아니므로이 일을하는 끔찍한 방법 일 수도 있지만 작동하는 것 같습니다.

다음은 수정 된 클래스의 :

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 
+0

그것을인가 여분 = 0 일 수도 있습니다. –

+1

@ 시바 - 방금 확인했는데 예 = 0을 추가 할 수 있습니다. 그러나 (내 경우에는) 주석을 원한다면 사용자에게 빈 양식을 주거나 필수로 지정해야합니다. – Kurt

4

@Daniel Roseman 솔루션은 괜찮하지만이 같은 행동을 할 몇 가지 적은 코드 몇 가지 수정을해야합니다.

class RequiredFormSet(forms.models.BaseInlineFormSet): 
     def __init__(self, *args, **kwargs): 
      super(RequiredFormSet, self).__init__(*args, **kwargs) 
      self.forms[0].empty_permitted = False 

class InvoiceOrderInline(admin.StackedInline): 
     model = InvoiceOrder 
     formset = RequiredFormSet 


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

이 그것도 작동합니다 :)

+0

아, 이걸 업 프리 한 것을 의미하지 않았다. "삭제"확인란을 선택하면 작동하지 않습니다. – Tobu

+0

질문을 이해하지 못하셨습니까? 이 코드는 모든 'Invoice'에는 반드시 하나의'InvoiceOrder'가 있어야합니다. 그리고 그때에는 삭제 체크 박스가 없습니다! – Ahsan