6👍
Just realized I never posted my solution back to this question. I ended up writing a model mixin to always run validation before saving; it’s a bit inconvenient as validation will technically be run twice in Django’s forms (i.e. in the admin panel), but it lets me guarantee that validation is run — regardless of what triggers a model save. I generally don’t use Django’s forms, so this doesn’t have much impact on my applications.
Here’s a quick snippet that does the trick:
class ValidatesOnSaveModelMixin:
""" ValidatesOnSaveModelMixin
A mixin that ensures valid model state prior to saving.
"""
def save(self, **kwargs):
self.full_clean()
super(ValidatesOnSaveModelMixin, self).save(**kwargs)
Here is how you’d use it:
class ImportantModel(ValidatesOnSaveModelMixin, models.Model):
""" Will always ensure its fields pass validation prior to saving. """
There is one important caveat: any of Django’s direct-to-database operations (i.e. ImportantModel.objects.update()
) don’t call a model’s save()
method, and therefore will not be validated. There’s not much to do about this, since these methods are really about optimizing performance by skipping a bunch of database calls — so just be aware of their impact if you use them.
4👍
I agree, the link between models/serializers/validation is broken.
The best DRY solution I’ve found is to keep validation in model, with validators specified on fields, then if needed, model level validation in clean()
overridden.
Then in serializer, override validate and call the model clean()
e.g. in MySerializer
:
def validate(self, data):
instance = FooModel(**data)
instance.clean()
return data
It’s not nice, but I prefer this to 2-level validation in serializer and model.
- [Django]-Checking the number of elements in an array in a Django template
- [Django]-How to put timedelta in django model?
- [Django]-How to generate temporary URLs in Django
1👍
Just wanted to add on SamuelMS’s answer.
In case you use F() expressions and similar. As explained here this will fail.
class ValidatesOnSaveModelMixin:
""" ValidatesOnSaveModelMixin
A mixin that ensures valid model state prior to saving.
"""
def save(self, **kwargs):
if 'clean_on_save_exclude' in kwargs:
self.full_clean(exclude=kwargs.pop('clean_on_save_exclude', None)
else:
self.full_clean()
super(ValidatesOnSaveModelMixin, self).save(**kwargs)
Then just use it the same way he explained.
And now when calling save, if you use query expressions can just call
instance.save(clean_on_save_exclude=['field_name'])
Just like you would exclude if you were calling full_clean and exclude the fields with query expressions.
See https://docs.djangoproject.com/en/2.2/ref/models/instances/#django.db.models.Model.full_clean
- [Django]-How to use MySQLdb with Python and Django in OSX 10.6?
- [Django]-How to add button next to Add User button in Django Admin Site
- [Django]-Requirements.txt greater than equal to and then less than?