Ticket #13878: 01-formset-refactoring.2.diff
File 01-formset-refactoring.2.diff, 8.9 KB (added by , 14 years ago) |
---|
-
django/forms/formsets.py
# HG changeset patch # Parent 6348119c628a728095e696dd606541752c32f7c8 diff --git a/django/forms/formsets.py b/django/forms/formsets.py
a b 42 42 self.initial = initial 43 43 self.error_class = error_class 44 44 self._errors = None 45 self._non_form_errors = None46 45 # construct the forms in the formset 47 46 self._construct_forms() 48 47 … … 152 151 return [form.cleaned_data for form in self.forms] 153 152 cleaned_data = property(_get_cleaned_data) 154 153 154 def _get_valid_forms(self): 155 """ 156 Returns a list of valid and filled forms, possibly in the order 157 specified by the incoming data (if ordering is allowed). 158 """ 159 if self._errors is None: 160 self.full_clean() 161 return self._valid_forms 162 valid_forms = property(_get_valid_forms) 163 155 164 def _get_deleted_forms(self): 156 165 """ 157 166 Returns a list of forms that have been marked for deletion. Raises an 158 167 AttributeError if deletion is not allowed. 159 168 """ 160 if not self.is_valid() or not self.can_delete: 161 raise AttributeError("'%s' object has no attribute 'deleted_forms'" % self.__class__.__name__) 162 # construct _deleted_form_indexes which is just a list of form indexes 163 # that have had their deletion widget set to True 164 if not hasattr(self, '_deleted_form_indexes'): 165 self._deleted_form_indexes = [] 166 for i in range(0, self.total_form_count()): 167 form = self.forms[i] 168 # if this is an extra form and hasn't changed, don't consider it 169 if i >= self.initial_form_count() and not form.has_changed(): 170 continue 171 if self._should_delete_form(form): 172 self._deleted_form_indexes.append(i) 173 return [self.forms[i] for i in self._deleted_form_indexes] 169 if self._errors is None: 170 self.full_clean() 171 return self._deleted_forms 174 172 deleted_forms = property(_get_deleted_forms) 175 173 176 174 def _get_ordered_forms(self): 177 175 """ 178 Returns a list of form in the order specified by the incoming data.176 Returns a list of forms in the order specified by the incoming data. 179 177 Raises an AttributeError if ordering is not allowed. 180 178 """ 181 if not self.is_valid() or not self.can_order: 182 raise AttributeError("'%s' object has no attribute 'ordered_forms'" % self.__class__.__name__) 183 # Construct _ordering, which is a list of (form_index, order_field_value) 184 # tuples. After constructing this list, we'll sort it by order_field_value 185 # so we have a way to get to the form indexes in the order specified 186 # by the form data. 187 if not hasattr(self, '_ordering'): 188 self._ordering = [] 189 for i in range(0, self.total_form_count()): 190 form = self.forms[i] 191 # if this is an extra form and hasn't changed, don't consider it 192 if i >= self.initial_form_count() and not form.has_changed(): 193 continue 194 # don't add data marked for deletion to self.ordered_data 195 if self.can_delete and self._should_delete_form(form): 196 continue 197 self._ordering.append((i, form.cleaned_data[ORDERING_FIELD_NAME])) 198 # After we're done populating self._ordering, sort it. 199 # A sort function to order things numerically ascending, but 200 # None should be sorted below anything else. Allowing None as 201 # a comparison value makes it so we can leave ordering fields 202 # blank. 203 def compare_ordering_key(k): 204 if k[1] is None: 205 return (1, 0) # +infinity, larger than any number 206 return (0, k[1]) 207 self._ordering.sort(key=compare_ordering_key) 208 # Return a list of form.cleaned_data dicts in the order spcified by 209 # the form data. 210 return [self.forms[i[0]] for i in self._ordering] 179 if self._errors is None: 180 self.full_clean() 181 return self._ordered_forms 211 182 ordered_forms = property(_get_ordered_forms) 212 183 213 184 #@classmethod … … 221 192 form -- i.e., from formset.clean(). Returns an empty ErrorList if there 222 193 are none. 223 194 """ 224 if self._ non_form_errors is notNone:225 return self._non_form_errors226 return self. error_class()195 if self._errors is None: 196 self.full_clean() 197 return self._non_form_errors 227 198 228 199 def _get_errors(self): 229 200 """ … … 245 216 246 217 def is_valid(self): 247 218 """ 248 Returns True if form .errors is empty for every form in self.forms.219 Returns True if form is valid. 249 220 """ 250 if not self.is_bound: 251 return False 252 # We loop over every form.errors here rather than short circuiting on the 253 # first failure to make sure validation gets triggered for every form. 254 forms_valid = True 255 for i in range(0, self.total_form_count()): 256 form = self.forms[i] 257 if self.can_delete: 258 if self._should_delete_form(form): 259 # This form is going to be deleted so any of its errors 260 # should not cause the entire formset to be invalid. 261 continue 262 if bool(self.errors[i]): 263 forms_valid = False 264 return forms_valid and not bool(self.non_form_errors()) 221 if self._errors is None: 222 self.full_clean() 223 return self._is_valid 265 224 266 225 def full_clean(self): 267 226 """ 268 Cleans all of self.data and populates self._errors. 227 Cleans all forms, populates self._errors, self._non_form_errors and 228 self._is_valid. If valid, populates also self._valid_forms, 229 self._ordered_forms (if can_order is True) and self._deleted_forms 230 (if can_delete is True). 269 231 """ 270 232 self._errors = [] 233 self._non_form_errors = self.error_class() 271 234 if not self.is_bound: # Stop further processing. 235 self._is_valid = False 272 236 return 273 for i in range(0, self.total_form_count()): 237 # Loop over every form here. 238 self._is_valid = True 239 valid_form_indexes, deleted_form_indexes = [], [] 240 for i in range(self.total_form_count()): 274 241 form = self.forms[i] 242 # Add errors from form. 275 243 self._errors.append(form.errors) 244 # Consider only forms that are initial or changed. 245 if i < self.initial_form_count() or form.has_changed(): 246 if self.can_delete and self._should_delete_form(form): 247 # Add index of form that should be deleted. 248 deleted_form_indexes.append(i) 249 else: 250 if form.is_valid(): 251 if self.can_order: 252 # Add index and order field value of valid form. 253 valid_form_indexes.append((i, form.cleaned_data[ORDERING_FIELD_NAME])) 254 else: 255 # Add index of valid form. 256 valid_form_indexes.append(i) 257 else: 258 # Mark formset as not valid but do not stop to make 259 # sure validation gets triggered for every form. 260 self._is_valid = False 276 261 # Give self.clean() a chance to do cross-form validation. 277 262 try: 278 263 self.clean() 279 264 except ValidationError, e: 280 265 self._non_form_errors = self.error_class(e.messages) 266 self._is_valid = False 267 # If formset is valid, populates self._valid_forms, 268 # self._ordered_forms (if can_order is True) and self._deleted_forms 269 # (if can_delete is True). 270 if self._is_valid: 271 if self.can_order: 272 # A sort function to order things numerically ascending, but 273 # None should be sorted below anything else. Allowing None as 274 # a comparison value makes it so we can leave ordering fields 275 # blank. 276 def compare_ordering_key(k): 277 if k[1] is None: 278 return (1, 0) # +infinity, larger than any number 279 return (0, k[1]) 280 valid_form_indexes.sort(key=compare_ordering_key) 281 self._valid_forms = [self.forms[i[0]] for i in valid_form_indexes] 282 self._ordered_forms = self._valid_forms 283 else: 284 self._valid_forms = [self.forms[i] for i in valid_form_indexes] 285 if self.can_delete: 286 self._deleted_forms = [self.forms[i] for i in deleted_form_indexes] 281 287 282 288 def clean(self): 283 289 """