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

时间:2022-10-22 10:47:31

I'm still trying to understand the correct way to validate a Django model object using a custom validator at the model level. I know that validation is usually done within a form or model form. However, I want to ensure the integrity of my data at the model level if I'm interacting with it via the ORM in the Python shell. Here's my current approach:

我仍然在尝试理解使用定制验证器在模型级别验证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)

Here are my questions:

这里是我的问题:

  1. Should I create a custom validation function, designate it as a validator, and then override the Person's save() function as I've done above? (By the way, I know I could validate my gender choices using the 'choices' field option but I created 'validate_gender' for the purpose of illustration).

    我是否应该创建一个自定义验证函数,将其指定为验证器,然后像我上面所做的那样覆盖Person的save()函数?(顺便说一下,我知道我可以使用“选择”字段选项验证我的性别选择,但我创建了“validate_gender”来进行演示)。

  2. If I really want to ensure the integrity of my data, should I not only write Django unit tests for testing at the model layer but also equivalent database-level unit tests using Python/Psycopg? I've noticed that Django unit tests, which raise ValidationErrors, only test the model's understanding of the database schema using a copy of the database. Even if I were to use South for migrations, any database-level constraints are limited to what Django can understand and translate into a Postgres constraint. If I need a custom constraint that Django can't replicate, I could potentially enter data into my database that violates that constraint if I'm interacting with the database directly via the psql terminal.

    如果我真的想确保数据的完整性,我是否应该不仅编写用于模型层测试的Django单元测试,还要编写使用Python/Psycopg的等效数据库级单元测试?我注意到,Django单元测试会引发ValidationErrors,它只使用数据库的副本测试模型对数据库模式的理解。即使我使用南方进行迁移,任何数据库级别的约束都限制在Django能够理解和转换为Postgres约束的地方。如果我需要一个Django不能复制的自定义约束,那么如果我直接通过psql终端与数据库交互,那么我可能会向数据库输入违反该约束的数据。

Thanks!

谢谢!

1 个解决方案

#1


17  

I had a similar misunderstanding of the ORM when I first started with Django.

当我第一次读《被解救的姜戈》时,我对ORM也有类似的误解。

1) No, don't put self.full_clean() inside of save. Either

1)不,不要将self.full_clean()放在save中。要么

A) use a ModelForm (which will cause all the same validation to occur - note: ModelForm.is_valid() won't call Model.full_clean explicitly, but will perform the exact same checks as Model.full_clean). Example:

A)使用ModelForm(它将导致所有相同的验证发生——注意:ModelForm.is_valid()不会调用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

Also note, forms aren't for use only in views that render them in templates - they're great for any sort of use, including an API, etc. After running form.is_valid() and getting errors, you'll have form.errors which is a dictionary containing all the errors in the form, including a key called '__all__' which will contain non-field errors.

同样要注意的是,表单并不仅仅用于在模板中呈现它们的视图中,它们对于任何类型的使用都很有用,包括API等。错误,即包含表单中所有错误的字典,包括一个名为“__all__”的键,它将包含非字段错误。

B) Simply use model_instance.full_clean() in your view (or other logical application layer), instead of using a form, but forms are a nice abstraction for this.

B)只需在视图(或其他逻辑应用程序层)中使用model_instance.full_clean(),而不是使用表单,但是表单是一个很好的抽象。

2) I don't really have a solution to, but I've never run into such a problem, even in large projects (the current project I work with my company on has 146 tables) and I don't suspect it'll be a concern in your case either.

我确实没有解决方案,但我从来没有遇到过这样的问题,即使是在大型项目中(我目前与我的公司合作的项目有表格),我也不认为这将是你的问题。

#1


17  

I had a similar misunderstanding of the ORM when I first started with Django.

当我第一次读《被解救的姜戈》时,我对ORM也有类似的误解。

1) No, don't put self.full_clean() inside of save. Either

1)不,不要将self.full_clean()放在save中。要么

A) use a ModelForm (which will cause all the same validation to occur - note: ModelForm.is_valid() won't call Model.full_clean explicitly, but will perform the exact same checks as Model.full_clean). Example:

A)使用ModelForm(它将导致所有相同的验证发生——注意:ModelForm.is_valid()不会调用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

Also note, forms aren't for use only in views that render them in templates - they're great for any sort of use, including an API, etc. After running form.is_valid() and getting errors, you'll have form.errors which is a dictionary containing all the errors in the form, including a key called '__all__' which will contain non-field errors.

同样要注意的是,表单并不仅仅用于在模板中呈现它们的视图中,它们对于任何类型的使用都很有用,包括API等。错误,即包含表单中所有错误的字典,包括一个名为“__all__”的键,它将包含非字段错误。

B) Simply use model_instance.full_clean() in your view (or other logical application layer), instead of using a form, but forms are a nice abstraction for this.

B)只需在视图(或其他逻辑应用程序层)中使用model_instance.full_clean(),而不是使用表单,但是表单是一个很好的抽象。

2) I don't really have a solution to, but I've never run into such a problem, even in large projects (the current project I work with my company on has 146 tables) and I don't suspect it'll be a concern in your case either.

我确实没有解决方案,但我从来没有遇到过这样的问题,即使是在大型项目中(我目前与我的公司合作的项目有表格),我也不认为这将是你的问题。