When I create new instances using forms, I know that calling is_valid on the form automatically runs the full_clean method for validation. After that, I just call save on the form.
But when I create new instances manually, I need to call full_clean manually before calling save.
I saw online that some people suggest overriding the save method in the model to ensure custom validation always runs, like this:
def save(self, *args, **kwargs):
self.full_clean()
super().save(*args, **kwargs)
This works fine when I’m creating instances manually, but it seems to call full_clean twice when I use forms (once during is_valid and once during save). This feels redundant and probably not efficient.
My Question:
What’s the best way to make sure my custom validation runs in Django models both when creating instances manually and via forms, without calling full_clean twice?
2
If you look in Django, method full_clean
is called only in forms/formsets, as a general interface for calling method clean of every choosen field in a Model.
In this case, creating an instance “manually”, calling full_clean
and then saving the instance is exactly the same as filling modelform with data, calling is_valid and then saving. The second option is preferable, because the form can perform some additional logic via the “formfieldcallback”, use additional validators and cleaners.
# pythonic way (not django way)
def create_instance():
data = {attr1=value1, attr2=value2}
instance = MyClass(**data)
try:
instance.full_clean()
except Exception as error:
raise CustomException("validation error") from error
instance.save()
return instance
But previous version is not Django-ish.
# Django way to create, validate and save instance
def create_instance():
data = {attr1=value1, attr2=value2}
form = modelform_factory(MyClass, **kwargs)(data=data)
if form.is_valid()
return form.save()
raise CustomException(f'validation errors: {form.errors}')
modelform_factory
– is a function, wich organize your ModelFrom
creation, and can pack different cleaners and validators into form “on-fly”. More here: https://docs.djangoproject.com/en/5.0/topics/forms/modelforms/#modelform-factory-function