验证Django模型对象的正确方法?

brvekthn  于 2023-06-25  发布在  Go
关注(0)|答案(2)|浏览(139)

我仍然在试图理解如何在模型级别使用自定义验证器来验证Django模型对象。我知道验证通常在表单或模型表单中完成。但是,如果我通过Python shell中的ORM与数据交互,我希望确保模型级别数据的完整性。以下是我目前的做法:

from django.db import models
from django.core import validators
from django.core exceptions import ValidationError

def validate_gender(value):
    """ Custom validator """
    if not value in ('m', 'f', 'M', 'F'):
        raise ValidationError(u'%s is not a valid value for gender.' % value)

class Person(models.Model):
    name = models.CharField(max_length=128)
    age = models.IntegerField()
    gender = models.CharField(maxlength=1, validators=[validate_gender])

    def save(self, *args, **kwargs):
        """ Override Person's save """
        self.full_clean(exclude=None)
        super(Person, self).save(*args, **kwargs)

以下是我的问题:
1.我是否应该创建一个自定义验证函数,将其指定为验证器,然后像上面所做的那样覆盖Person的保存()函数?(顺便说一下,我知道我可以使用'choices'字段选项验证我的性别选择,但我创建了'validate_gender'用于说明目的)。
1.如果我真的想确保数据的完整性,我不仅应该在模型层编写Django单元测试,还应该使用Python/Psycopg编写等效的数据库级单元测试?我注意到Django单元测试会引发ValidationErrors,它只使用数据库的副本来测试模型对数据库模式的理解。即使我使用South进行迁移,任何数据库级别的约束也仅限于Django能够理解并转换为Postgres约束的内容。如果我需要一个Django无法复制的自定义约束,如果我直接通过psql终端与数据库交互,我可能会向数据库输入违反该约束的数据。
谢谢!

ttcibm8c

ttcibm8c1#

当我刚开始使用Django时,我对ORM也有类似的误解。
1.不,不要把self.full_clean()放在save里面。要么
A)使用ModelForm(这将导致所有相同的验证发生-注意:ModelForm.is_valid()不会显式调用Model.full_clean,但会执行与Model.full_clean完全相同的检查)。示例:

class PersonForm(forms.ModelForm):
    class Meta:
        model = Person

def add_person(request):
    if request.method == 'POST':
        form = PersonForm(request.POST, request.FILES)
        if form.is_valid():  # Performs your validation, including ``validate_gender``
            person = form.save()
            return redirect('some-other-view')
    else:
        form = PersonForm()
        # ... return response with ``form`` in the context for rendering in a template

还要注意,表单并不仅仅用于在模板中呈现它们的视图中--它们非常适合任何类型的使用,包括API等。运行form.is_valid()并获取错误后,您将获得form.errors,这是一个包含表单中所有错误的字典,包括一个名为'__all__'的键,它将包含非字段错误。
B)在视图(或其他逻辑应用层)中简单地使用model_instance.full_clean(),而不是使用表单,但表单是一个很好的抽象。
1.我真的没有一个解决方案,但我从来没有遇到过这样的问题,即使是在大型项目中(我目前与公司合作的项目有146个表),我也不怀疑这会成为您的问题。

0tdrvxhp

0tdrvxhp2#

从django 4.1开始,self.full_clean默认也运行self.validate_constraints(docs)。
在makemigrations之后,迁移中将包含一个migrations.AddConstraint语句。
在您的案例中:

from django.db import models

class Person(models.Model):
    class Gender(models.TextChoices):
        M = "M", "Male"
        F = "F", "Female"

    name = models.CharField(max_length=128)
    age = models.IntegerField()
    gender = models.CharField(maxlength=1, choices=Gender.choices)

    class Meta:
        constraints = [
            models.CheckConstraint(
                check=models.Q(gender__in=Gender.values), name="valid_gender"
            ),
            models.CheckConstraint(
                check=models.Q(age__gte=18), name="adults_only"
            ),
        ]

运行迁移后,如果您现在尝试self.save(),将引发IntegrityError

相关问题