Opened 14 years ago
Closed 14 years ago
#14420 closed (wontfix)
Can't do validation on ModelFormSets to prevent deletion when there is only one object in formset and it is to be deleted
Reported by: | Rory McCann | Owned by: | nobody |
---|---|---|---|
Component: | Forms | Version: | 1.1 |
Severity: | Keywords: | validation, formset, modelformset | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
I'm using a ModelFormSet (using modelformset_factory and a custom formset param) to change and delete objects. I want to do some validation, to prevent objects being deleted if they match certain criteria. I put this validation code in the clean() method of the custom formset object (which is a subclass of BaseModelFormSet). It loops over self.deleted_forms and raises a ValidationError if any of the forms to be deleted matches this criteria.
However this doesn't work if there is only one object/form in the formset, and you want to delete that one object, and that object should fail your 'don't delete objects like this' test. The FormSet code shortcircuits the validation (since you should be allowed to delete an object without making it valid first), and doesn't call self.errors (on the FormSet object), and doesn't call the (custom) clean() method. The is_valid() method of the (bound) FormSet returns True, when it should return False. The non_form_errors() returns an empty list, when it should have the errors saying that you can't delete this object. You can then .save() this formset and delete the object that you shouldn't be able to delete.
A simple work around I am using is to call formset.full_clean() just after initialising the formset (i.e. before calling formset.is_valid()). This calls all the clean() methods of all the forms and of the FormSet.
I'm not convinced this is a bug, since it depends on using validation errors to prevent deletion. You point at a specific bug in the 'one object' case, but if I understand the code correctly, the same bug will exist if you mark every row for deletion, since as you note, you don't need to make an object valid in order to delete it, and if there's nothing to validate, clean() won't get called on the formset. In order to fix the behavior you describe, we would need to change this fundamental assertion about when validation will be performed.
It seems to me like this should be handled by controlling the can_delete attribute of sub_forms, rather than trying to evaluate deletion properties at the formset level.
If you feel strongly about this, feel free to start a discussion on django-developers and make your case.