Ticket #5353: 5353-BaseFormSet.clean_with_simple_tests.patch
File 5353-BaseFormSet.clean_with_simple_tests.patch, 9.9 KB (added by , 17 years ago) |
---|
-
django/newforms/formsets.py
commit 62fad9020ac67500d0b5f4fe026aa47def91a7ba Author: Honza Král <Honza.Kral@gmail.com> Date: Sat Sep 15 01:18:31 2007 +0200 http://code.djangoproject.com/ticket/5353 diff --git a/django/newforms/formsets.py b/django/newforms/formsets.py index 2a0a131..3d05877 100644
a b class BaseFormSet(object): 93 93 forms = property(_forms) 94 94 95 95 def full_clean(self): 96 """Cleans all of self.data and populates self._ _errors and self.cleaned_data."""96 """Cleans all of self.data and populates self._is_valid and self.non_form_errors if applicable.""" 97 97 is_valid = True 98 errors = []98 self.non_form_errors = [] 99 99 if not self.is_bound: # Stop further processing. 100 self.__errors = errors101 100 return 102 cleaned_data = []103 deleted_data = []104 101 105 102 # Process change forms 106 103 for form in self.change_forms: 107 if form.is_valid(): 108 if self.deletable and form.cleaned_data[DELETION_FIELD_NAME]: 109 deleted_data.append(form.cleaned_data) 110 else: 111 cleaned_data.append(form.cleaned_data) 112 else: 104 if not form.is_valid(): 113 105 is_valid = False 114 errors.append(form.errors)115 106 116 107 # Process add forms in reverse so we can easily tell when the remaining 117 108 # ones should be required. 118 109 required = False 119 add_errors = []120 110 for i in range(len(self.add_forms)-1, -1, -1): 121 111 form = self.add_forms[i] 122 112 # If an add form is empty, reset it so it won't have any errors 123 113 if form.is_empty([ORDERING_FIELD_NAME]) and not required: 124 114 form.reset() 125 continue126 115 else: 127 116 required = True 128 if form.is_valid(): 129 cleaned_data.append(form.cleaned_data) 130 else: 117 if not form.is_valid(): 131 118 is_valid = False 132 add_errors.append(form.errors)133 add_errors.reverse()134 errors.extend(add_errors)135 119 136 if self.orderable: 137 cleaned_data.sort(lambda x,y: x[ORDERING_FIELD_NAME] - y[ORDERING_FIELD_NAME]) 120 try: 121 self.clean() 122 except ValidationError, e: 123 self.non_form_errors.extend(e.messages) 124 is_valid = False 138 125 139 if is_valid:140 self.cleaned_data = cleaned_data141 self.deleted_data = deleted_data142 self.errors = errors143 126 self._is_valid = is_valid 144 127 128 def forms_to_add(self): 129 return [f for f in self.add_forms if f.is_bound] 130 forms_to_add = property(forms_to_add) 131 132 def deleted_forms(self): 133 """return all the forms schedulet for deletion""" 134 if not self.is_valid() or not self.deletable: 135 raise AttributeError, "%r object has no attribute 'deleted_forms'" % self.__class__.__name__ 136 if not hasattr( self, '_deleted_forms' ): 137 self._deleted_forms = [f for f in self.change_forms if f.cleaned_data[DELETION_FIELD_NAME]] 138 return self._deleted_forms 139 deleted_forms = property(deleted_forms) 140 141 def sorted_forms(self): 142 """return a list of sorted forms, when orderable""" 143 if not self.is_valid() or not self.orderable: 144 raise AttributeError, "%r object has no attribute 'sorted_forms'" % self.__class__.__name__ 145 if not hasattr(self, '_sorted_forms'): 146 if self.deletable: 147 self._sorted_forms = [f for f in self.change_forms if not f.cleaned_data[DELETION_FIELD_NAME]] + self.forms_to_add 148 else: 149 self._sorted_forms = self.change_forms + self.forms_to_add 150 self._sorted_forms.sort(lambda x,y: x.cleaned_data[ORDERING_FIELD_NAME] - y.cleaned_data[ORDERING_FIELD_NAME]) 151 return self._sorted_forms 152 sorted_forms = property(sorted_forms) 153 154 def has_errors(self): 155 return not self.is_valid() 156 157 def clean(self): 158 pass 159 145 160 def add_fields(self, form, index): 146 161 """A hook for adding extra fields on to each form instance.""" 147 162 if self.orderable: -
tests/regressiontests/forms/formsets.py
diff --git a/tests/regressiontests/forms/formsets.py b/tests/regressiontests/forms/formsets.py index 5129c15..0118dba 100644
a b errors will be a list of dicts rather than just a single dict. 45 45 >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 46 46 >>> formset.is_valid() 47 47 True 48 >>> formset.cleaned_data 48 >>> formset.sorted_forms 49 Traceback (most recent call last): 50 ... 51 AttributeError: 'ChoiceFormSet' object has no attribute 'sorted_forms' 52 >>> formset.deleted_forms 53 Traceback (most recent call last): 54 ... 55 AttributeError: 'ChoiceFormSet' object has no attribute 'deleted_forms' 56 >>> [f.cleaned_data for f in formset.forms_to_add ] 49 57 [{'votes': 100, 'choice': u'Calexico'}] 50 58 51 59 If a FormSet was not passed any data, its is_valid method should return False. … … any of the forms. 65 73 >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 66 74 >>> formset.is_valid() 67 75 False 68 >>> formset.errors76 >>> [f.errors for f in formset.forms_to_add] 69 77 [{'votes': [u'This field is required.']}] 70 78 71 Like a Form instance, cleaned_data won't exist if the formset wasn't validated.72 73 >>> formset.cleaned_data74 Traceback (most recent call last):75 ...76 AttributeError: 'ChoiceFormSet' object has no attribute 'cleaned_data'77 78 79 79 We can also prefill a FormSet with existing data by providing an ``initial`` 80 80 argument to the constructor. ``initial`` should be a list of dicts. By default, 81 81 an extra blank form is included. … … Let's simulate what would happen if we submitted this form. 103 103 >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 104 104 >>> formset.is_valid() 105 105 True 106 >>> formset.cleaned_data106 >>> [f.cleaned_data for f in formset.change_forms + formset.forms_to_add ] 107 107 [{'votes': 100, 'choice': u'Calexico'}] 108 108 109 109 But the second form was blank! Shouldn't we get some errors? No. If we display … … handle that later. 123 123 >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 124 124 >>> formset.is_valid() 125 125 False 126 >>> formset.errors127 [{ }, {'votes': [u'This field is required.']}]126 >>> [f.errors for f in formset.forms_to_add] 127 [{'votes': [u'This field is required.']}] 128 128 129 129 130 130 If we delete data that was pre-filled, we should get an error. Simply removing … … handle that case later. 142 142 >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 143 143 >>> formset.is_valid() 144 144 False 145 >>> formset.errors145 >>> [f.errors for f in formset.change_forms] 146 146 [{'votes': [u'This field is required.'], 'choice': [u'This field is required.']}] 147 147 148 148 … … number of forms to be completed. 180 180 >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 181 181 >>> formset.is_valid() 182 182 True 183 >>> formset. cleaned_data183 >>> formset.forms_to_add 184 184 [] 185 185 186 186 … … We can just fill out one of the forms. 199 199 >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 200 200 >>> formset.is_valid() 201 201 True 202 >>> formset.cleaned_data202 >>> [f.cleaned_data for f in formset.forms_to_add] 203 203 [{'votes': 100, 'choice': u'Calexico'}] 204 204 205 205 … … And once again, if we try to partially complete a form, validation will fail. 218 218 >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 219 219 >>> formset.is_valid() 220 220 False 221 >>> formset.errors221 >>> [f.errors for f in formset.forms_to_add] 222 222 [{}, {'votes': [u'This field is required.']}] 223 223 224 224 … … get an error. 254 254 ... 'choices-3-votes': '', 255 255 ... } 256 256 257 >>> formset = ChoiceFormSet(data, auto_id=False,prefix='choices')257 >>> formset = ChoiceFormSet(data, prefix='choices') 258 258 >>> formset.is_valid() 259 259 False 260 >>> formset.errors260 >>> [f.errors for f in formset.forms_to_add] 261 261 [{}, {'votes': [u'This field is required.'], 'choice': [u'This field is required.']}, {}] 262 262 263 263 … … To delete something, we just need to set that form's special delete field to 303 303 >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 304 304 >>> formset.is_valid() 305 305 True 306 >>> formset.cleaned_data306 >>> [f.cleaned_data for f in formset.change_forms if not f in formset.deleted_forms] 307 307 [{'votes': 100, 'DELETE': False, 'choice': u'Calexico'}] 308 >>> formset.deleted_data308 >>> [f.cleaned_data for f in formset.deleted_forms] 309 309 [{'votes': 900, 'DELETE': True, 'choice': u'Fergie'}] 310 310 311 311 # FormSets with ordering ###################################################### … … something at the front of the list, you'd need to set it's order to 0. 350 350 >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 351 351 >>> formset.is_valid() 352 352 True 353 >>> for cleaned_data in formset.cleaned_data:354 ... print cleaned_data353 >>> for f in formset.sorted_forms: 354 ... print f.cleaned_data 355 355 {'votes': 500, 'ORDER': 0, 'choice': u'The Decemberists'} 356 356 {'votes': 100, 'ORDER': 1, 'choice': u'Calexico'} 357 357 {'votes': 900, 'ORDER': 2, 'choice': u'Fergie'} … … Let's delete Fergie, and put The Decemberists ahead of Calexico. 412 412 >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 413 413 >>> formset.is_valid() 414 414 True 415 >>> for cleaned_data in formset.cleaned_data:416 ... print cleaned_data415 >>> for f in formset.sorted_forms: 416 ... print f.cleaned_data 417 417 {'votes': 500, 'DELETE': False, 'ORDER': 0, 'choice': u'The Decemberists'} 418 418 {'votes': 100, 'DELETE': False, 'ORDER': 1, 'choice': u'Calexico'} 419 >>> formset.deleted_data419 >>> [f.cleaned_data for f in formset.deleted_forms] 420 420 [{'votes': 900, 'DELETE': True, 'ORDER': 2, 'choice': u'Fergie'}] 421 421 422 422 423 424 >>> def clean(self): 425 ... from django import newforms as forms 426 ... raise forms.ValidationError, 'XXX' 427 >>> setattr(ChoiceFormSet, 'clean', clean) 428 >>> formset = ChoiceFormSet(data, auto_id=False, prefix='choices') 429 >>> formset.is_valid() 430 False 431 >>> formset.non_form_errors 432 [u'XXX'] 433 423 434 """