在Django REST框架中,为什么序列化程序正确地处理模型中的字段级验证异常,而不是对象级验证?

在Django REST框架中,为什么序列化程序正确地处理模型中的字段级验证异常,而不是对象级验证?,django,validation,django-rest-framework,django-serializer,Django,Validation,Django Rest Framework,Django Serializer,为了说明我的问题,假设我有一个简单的Person模型,定义如下: from django.db import models from django.core.validators import MinLengthValidator, MaxLengthValidator, ValidationError class Person(models.Model): first_name = models.CharField(max_length=100, null=False, blank=

为了说明我的问题,假设我有一个简单的
Person
模型,定义如下:

from django.db import models
from django.core.validators import MinLengthValidator, MaxLengthValidator, ValidationError

class Person(models.Model):
    first_name = models.CharField(max_length=100, null=False, blank=False,
                                  validators=[MinLengthValidator(limit_value=1),
                                              MaxLengthValidator(limit_value=100)])
    last_name = models.CharField(max_length=100, null=True, blank=True,
                                 validators=[MinLengthValidator(limit_value=1),
                                             MaxLengthValidator(limit_value=100)])

    def clean(self):
        self.validate()
        super().clean()

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

    def validate(self):
        """The first and last names cannot be the same strings."""
        if (self.first_name and self.last_name and
                self.first_name.lower() == self.last_name.lower()):
            raise ValidationError('First and last names, if both are provided, cannot be the same.',
                                  code='invalid',
                                  params={'first_name': self.first_name,
                                          'last_name': self.last_name})
  • 请注意,
    first\u name
    last\u name
    字段都具有与之关联的字段级验证。(我的开发数据库是SQLite,它不做长度验证。所以我不得不添加
    验证程序
    。但这不是我的问题。)
我定义了两个简单的基于APIView的类:

from rest_framework import generics
from ..models import Person
from ..serializers import PersonSerializer

class PersonDetailView(generics.RetrieveUpdateDestroyAPIView):
    name = 'person-detail'
    queryset = Person.objects.all()
    serializer_class = PersonSerializer
    lookup_field = 'id'

class PersonListView(generics.ListCreateAPIView):
    name = 'person-list'
    queryset = Person.objects.all()
    serializer_class = PersonSerializer
    lookup_field = 'id'
我基于Django REST框架的
ModelSerializer
定义了一个序列化程序:

from rest_framework import serializers
from ..models import Person

class PersonSerializer(serializers.ModelSerializer):
    class Meta:
        model = Person
        fields = ('id', 'first_name', 'last_name')
我还映射了一些URL

如果我发布、放置或修补程序违反了字段级验证规则(例如,我尝试提交一个101个字符长的名字),Django REST框架将捕获模型类中的异常并适当地显示它。以下是可浏览API中的外观:

但是,如果我发布、放置或修补程序违反了对象级别验证规则,Django REST framework不会捕获异常,服务器崩溃并显示如下跟踪:

from django.db import models
from django.core.validators import MinLengthValidator, MaxLengthValidator, ValidationError

class Person(models.Model):
    first_name = models.CharField(max_length=100, null=False, blank=False,
                                  validators=[MinLengthValidator(limit_value=1),
                                              MaxLengthValidator(limit_value=100)])
    last_name = models.CharField(max_length=100, null=True, blank=True,
                                 validators=[MinLengthValidator(limit_value=1),
                                             MaxLengthValidator(limit_value=100)])

    def clean(self):
        self.validate()
        super().clean()

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

    def validate(self):
        """The first and last names cannot be the same strings."""
        if (self.first_name and self.last_name and
                self.first_name.lower() == self.last_name.lower()):
            raise ValidationError('First and last names, if both are provided, cannot be the same.',
                                  code='invalid',
                                  params={'first_name': self.first_name,
                                          'last_name': self.last_name})

我的解决方案是向序列化程序和模型添加对象级验证。下面是序列化程序及其自己的
validate
方法:

from rest_framework import serializers
from rest_framework.validators import ValidationError

from ..models import Person

class PersonSerializer(serializers.ModelSerializer):
    class Meta:
        model = Person
        fields = ('id', 'first_name', 'last_name')

    def validate(self, attrs):
        first_name = attrs.get('first_name')
        last_name = attrs.get('last_name')
        if first_name and last_name and first_name.lower() == last_name.lower():
            raise ValidationError('First and last names must be different.',
                                  code='invalid')
        return attrs
如果我这样做,那么Django REST框架可以很好地处理异常:

这里(最后;-)是我的问题:

为什么我必须在模型和序列化程序中执行对象级验证,而我只需要在模型中执行字段级验证,并且序列化程序可以很好地处理异常

Django REST框架就是这样运行的吗?似乎序列化程序应该能够优雅地处理模型引发的所有
ValidationError
s


是的。您需要在序列化程序中放置一个方法:

class PersonSerializer(serializer.ModelSerializer):

    def validate_first_name(self, value):
        if len(value)> 100:
            raise serializer.ValidationError("Can't be more than 100")
        return value

在DRF 3.0之后,
.clean()
方法不会作为序列化程序验证的一部分被调用,就像使用ModelForm一样,请阅读此文。

我很好奇。为什么DRF不能处理对象级的验证规则?方向舵——这正是我的观点。当序列化程序可以很好地处理来自模型的字段级验证异常时,为什么我必须在模型和序列化程序中都有一个验证方法?序列化程序只能处理模型中的对象级验证异常。@SeanFrancisN.Ballais它处理对象级验证:。至于@SteveWehba的观点,嗯,不幸的是drf不能处理django核心验证错误。但它处理restframework.serializer.validationerror。你们仍然可以从模型清理方法中抛出它,不管怎样,它都会起作用。好的,如果您想将这些验证放在模型中并抛出django验证异常,那么您需要将
serializer.save()
块放在一个try catch中。这是一个关键的细节-该链接甚至提供了关于如何解决问题的代码