2009-05-18 18 views
41

Chciałbym wprowadzić obowiązkowy cały formularz wbudowany w formularzu zmiany administratora. Tak więc w moim bieżącym scenariuszu, kiedy zapisuję dane w formularzu faktury (w Admin), formularz zamówienia w formularzu jest pusty. Chciałbym, aby ludzie nie tworzyli faktur bez powiązanych zamówień.Sprawdzanie poprawności formularzy w formularzu w Django

Ktoś wie, w jaki prosty sposób to zrobić?

Prawidłowa weryfikacja, taka jak (required=True) w polu modelu, nie działa w tym przypadku.

Odpowiedz

63

Najlepszym sposobem na to jest zdefiniowanie niestandardowego zestawu formularzy za pomocą czystej metody potwierdzającej, że istnieje co najmniej jedna kolejność faktur.

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

idealne rozwiązanie, dzięki – user108791

+3

stwierdziliśmy, że jeśli pole kasowania jest zaznaczone, że to możliwe, aby potwierdzić z 0 zleceń. Zobacz moją odpowiedź na poprawioną klasę, która rozwiązuje ten problem. –

+0

Dziękuję bardzo za tę poprawkę (i Dan za ulepszenie). Jako możliwą wskazówkę dla innych zrobiłem "klasę MandatoryInlineFormSet (BaseInlineFormSet)", a następnie wyprowadziłem z tego InvoiceAdminFormSet. W moim InvoiceAdminFormSet mam metodę clean(), która wykonuje niestandardową weryfikację, ale najpierw wywołuje funkcję MandatoryInlineFromSet.clean(). – Kurt

18

odpowiedź Daniela jest doskonała i pracował dla mnie na jednym projekcie, ale potem zdałem sobie sprawę, ze względu na sposób Django tworzy dzieło, jeśli używasz can_delete i zaznacz pole usuwania podczas zapisywania, to jest możliwe do sprawdzania wag/o dowolne zamówienia (w tym przypadku).

Spędziłem chwilę próbując dowiedzieć się, jak temu zapobiec. Pierwsza sytuacja była prosta - nie dołączaj formularzy, które zostaną usunięte w liczniku. Druga sytuacja była trudniejsza ... jeśli wszystkie pola wyboru są zaznaczone,nie został wywołany.

Kod nie jest dokładnie nieskomplikowany, niestety. Metoda clean jest wywoływana z full_clean, która jest wywoływana, gdy uzyskuje się dostęp do właściwości error. Ta właściwość nie jest dostępna podczas usuwania subformularza, więc full_clean nigdy nie jest wywoływana. Nie jestem ekspertem od Django, więc może to być okropny sposób, ale wygląda na to, że działa.

Oto zmodyfikowana klasa:

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

Czy go można zrobić to samo z extra = 0? –

+1

@Siva - Właśnie sprawdziłem i tak, możesz mieć dodatkowe = 0. Jeśli jednak chcesz, aby komentarz (w moim przypadku) był obowiązkowy, prawdopodobnie powinieneś dać mu pusty formularz lub nie uczynić go obowiązkowym. – Kurt

4

rozwiązanie @Daniel Roseman jest w porządku, ale mam pewną modyfikację z mniej kodu, aby zrobić to samo.

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] 

spróbować też działa :)

+0

Ups, nie chciał tego upomnieć. Nie działa, gdy zaznaczone są pola wyboru "Usuń". – Tobu

+0

nie rozumiałeś twojego pytania? ten kod zapewnia, że ​​każda "Faktura" musi mieć w sobie jedną "Fakturę". I w tym czasie nie ma żadnych zaznaczeń! – Ahsan

Powiązane problemy