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

时间:2010-12-14 16:32:36

标签: python django django-models django-validation

我很好奇是否有人知道django的orm是否有充分理由不在模型上调用'full_clean',除非它被保存为模型表单的一部分。

  

请注意,调用模型的save()方法时,不会自动调用full_clean()。如果要为自己手动创建的模型运行一步模型验证,则需要手动调用它。   django's full clean doc

(注意:为Django 1.6更新了引用...以前的django文档也有关于ModelForms的警告。)

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

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

7 个答案:

答案 0 :(得分:50)

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

您可能感兴趣的来源:

答案 1 :(得分:25)

由于兼容性考虑,在django内核中没有启用自动保存保存。

如果我们要开始一个新项目并希望模型上的默认save方法可以自动清理,我们可以在保存每个模型之前使用以下信号进行清理。

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 :(得分:10)

调用full_clean方法的最简单方法就是覆盖save中的model方法:

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

答案 3 :(得分:1)

如果您要确保模型至少具有一个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

答案 4 :(得分:1)

评论@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()

答案 5 :(得分:0)

我们可以将应用作为INSTALLED_APPS

中的settings.py部分,而不是插入一段声明接收器的代码
INSTALLED_APPS = [
    # ...
    'django_fullclean',
    # your apps here,
]

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

pip install django-fullclean

答案 6 :(得分:0)

如果您想始终确保模型验证,全局 pre_save 信号可以很好地工作。但是,它会在当前版本 (3.1.x) 中遇到 Django 身份验证问题,并可能导致您正在使用的其他应用中的模型出现问题。

详细说明@Peter Shannon 的回答,此版本只会验证您在其中执行它的模块内的模型,使用 "raw" saves 跳过验证并将 dispatch_uid 添加到 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')