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|
|Severity:||Keywords:||validation, formset, modelformset|
|Has patch:||no||Needs documentation:||no|
|Needs tests:||no||Patch needs improvement:||no|
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.