68👍
AFAIK, this is because of backwards compatibility. There are also problems with ModelForms with excluded fields, models with default values, pre_save() signals, etc.
Sources you might be intrested in:
37👍
Because of the compatibility considering, the auto clean on save is not enabled in django kernel.
If we are starting a new project and want the default save
method on Model could clean automatically, we can use the following signal to do clean before every model was saved.
from django.dispatch import receiver
from django.db.models.signals import pre_save, post_save
@receiver(pre_save)
def pre_save_handler(sender, instance, *args, **kwargs):
instance.full_clean()
- [Django]-How to access Enum types in Django templates
- [Django]-Django Background Task
- [Django]-Django admin TabularInline – is there a good way of adding a custom html column?
23👍
The simplest way to call the full_clean
method is just to override the save
method in your model:
class YourModel(models.Model):
...
def save(self, *args, **kwargs):
self.full_clean()
return super(YourModel, self).save(*args, **kwargs)
- [Django]-How do I do an OR filter in a Django query?
- [Django]-Malformed Packet: Django admin nested form can't submit, connection was reset
- [Django]-Referencing multiple submit buttons in django
4👍
Commenting on @Alfred Huang’s answer and coments on it. One might lock the pre_save hook down to an app by defining a list of classes in the current module (models.py) and checking against it in the pre_save hook:
CUSTOM_CLASSES = [obj for name, obj in
inspect.getmembers(sys.modules[__name__])
if inspect.isclass(obj)]
@receiver(pre_save)
def pre_save_handler(sender, instance, **kwargs):
if type(instance) in CUSTOM_CLASSES:
instance.full_clean()
- [Django]-How to specify an IP address with Django test client?
- [Django]-Django models: mutual references between two classes and impossibility to use forward declaration in python
- [Django]-Django model constraint for related objects
4👍
A global pre_save
signal can work well if you want to always ensure model validation. However it will run into issues with Django’s auth in current versions (3.1.x) and could cause issues with models from other apps you are using.
Elaborating on @Peter Shannon’s answer, this version will only validate models inside the module you execute it in, skips validation with "raw" saves and adds a dispatch_uid
to avoid duplicate signals.
from django.db.models.signals import pre_save
import inspect
import sys
MODELS = [obj for name, obj in
inspect.getmembers(sys.modules[__name__], inspect.isclass)]
def validate_model(sender, instance, **kwargs):
if 'raw' in kwargs and not kwargs['raw']:
if type(instance) in MODELS:
instance.full_clean()
pre_save.connect(validate_model, dispatch_uid='validate_models')
- [Django]-Django.contrib.gis.db.backends.postgis vs django.db.backends.postgresql_psycopg2
- [Django]-How to force application version on AWS Elastic Beanstalk
- [Django]-How do you use the django-filter package with a list of parameters?
2👍
Instead of inserting a piece of code that declares a receiver, we can use an app as INSTALLED_APPS
section in settings.py
INSTALLED_APPS = [
# ...
'django_fullclean',
# your apps here,
]
Before that, you may need to install django-fullclean
using PyPI:
pip install django-fullclean
- [Django]-How do I get the object if it exists, or None if it does not exist in Django?
- [Django]-Uninstall Django completely
- [Django]-Django admin default filter
2👍
If you have a model that you want to ensure has at least one FK relationship, and you don’t want to use null=False
because that requires setting a default FK (which would be garbage data), the best way I’ve come up with is to add custom .clean()
and .save()
methods. .clean()
raises the validation error, and .save()
calls the clean. This way the integrity is enforced both from forms and from other calling code, the command line, and tests. Without this, there is (AFAICT) no way to write a test that ensures that a model has a FK relation to a specifically chosen (not default) other model.
class Payer(models.Model):
name = models.CharField(blank=True, max_length=100)
# Nullable, but will enforce FK in clean/save:
payer_group = models.ForeignKey(PayerGroup, null=True, blank=True,)
def clean(self):
# Ensure every Payer is in a PayerGroup (but only via forms)
if not self.payer_group:
raise ValidationError(
{'payer_group': 'Each Payer must belong to a PayerGroup.'})
def save(self, *args, **kwargs):
self.full_clean()
return super().save(*args, **kwargs)
def __str__(self):
return self.name
- [Django]-Django Server Error: port is already in use
- [Django]-Choose test database?
- [Django]-Django: Safely Remove Old Migrations?