django的model.save()为什么不调用full_clean()?

问题:django的model.save()为什么不调用full_clean()?

我只是好奇是否有人知道,除非有理由将django的orm不保存在模型中,否则它不会在模型上调用“ full_clean”。

请注意,当您调用模型的save()方法时,不会自动调用full_clean()。若要为自己创建的模型运行一步模型验证,则需要手动调用它。 django的完整档案

(注意:报价已针对Django 1.6更新…之前的django文档也对ModelForms提出了警告。)

人们为什么不希望这种行为有充分的理由?我想如果您花时间向模型添加验证,则希望每次保存模型时都运行验证。

我知道如何使一切正常工作,我只是在寻找一种解释。

I’m just curious if anyone knows if there’s good reason why django’s orm doesn’t call ‘full_clean’ on a model unless it is being saved as part of a model form.

Note that full_clean() will not be called automatically when you call your model’s save() method. You’ll need to call it manually when you want to run one-step model validation for your own manually created models. django’s full clean doc

(NOTE: quote updated for Django 1.6… previous django docs had a caveat about ModelForms as well.)

Are there good reasons why people wouldn’t want this behavior? I’d think if you took the time to add validation to a model, you’d want that validation run every time the model is saved.

I know how to get everything to work properly, I’m just looking for an explanation.


回答 0

AFAIK,这是因为向后兼容。带有排除字段的ModelForms,具有默认值的模型,pre_save()信号等也存在问题。

您可能感兴趣的资源:

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:


回答 1

由于考虑到兼容性,因此在django内核中未启用保存时自动清除。

如果我们正在启动一个新项目,并且希望saveModel上的默认方法可以自动清除,则可以在保存每个模型之前使用以下信号进行清除。

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()

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()

回答 2

调用该full_clean方法的最简单方法是save在您的方法中覆盖该方法model

def save(self, *args, **kwargs):
    self.full_clean()
    return super(YourModel, self).save(*args, **kwargs)

The simpliest way to call the full_clean method is just to override save method in your model:

def save(self, *args, **kwargs):
    self.full_clean()
    return super(YourModel, self).save(*args, **kwargs)

回答 3

除了插入件的代码,声明了一个接收器的,我们可以使用一个应用程序如INSTALLED_APPS在节settings.py

INSTALLED_APPS = [
    # ...
    'django_fullclean',
    # your apps here,
]

在此之前,您可能需要django-fullclean使用PyPI 安装:

pip install django-fullclean

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

回答 4

如果您要确保具有至少一个FK关系的模型,并且不想使用该模型,null=False因为这需要设置默认FK(这将是垃圾数据),那么我想出的最好方法是添加自定义.clean().save()方法。.clean()引发验证错误,并.save()调用clean。这样,可以从表单以及其他调用代码,命令行和测试中强制执行完整性。没有这个,就无法(AFAICT)编写测试来确保模型与特定选择的(非默认)其他模型具有FK关系。

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

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

回答 5

评论@Alfred Huang的回答和评论。可以通过在当前模块(models.py)中定义一个类列表并在pre_save钩子中对其进行检查来将pre_save钩子锁定到应用程序:

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()

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()