174

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

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

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

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

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

4

7 回答 7

64

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

您可能感兴趣的来源:

于 2010-12-14T16:47:58.353 回答
34

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

如果我们正在开始一个新项目并希望 Model 上的默认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()
于 2015-09-07T05:56:38.870 回答
20

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)
于 2016-08-12T09:57:44.020 回答
3

评论@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()
于 2019-10-29T03:58:33.400 回答
3

INSTALLED_APPS我们可以将应用程序用作部分,而不是插入一段声明接收器的代码settings.py

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

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

pip install django-fullclean
于 2016-06-14T22:37:12.757 回答
2

如果您有一个要确保至少具有一个 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
于 2017-09-12T17:34:00.993 回答
0

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

详细说明@Peter Shannon 的答案,此版本将仅验证您执行它的模块内的模型,使用“原始”保存跳过验证并添加 adispatch_uid避免重复信号

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')
于 2021-03-01T19:35:51.730 回答