DRF:创建时验证嵌套的序列化器数据,但更新时不验证 [英] DRF: Validate nested serializer data when creating, but not when updating

查看:370
本文介绍了DRF:创建时验证嵌套的序列化器数据,但更新时不验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在DRF中使用可写的嵌套序列化器时,存在一个已知的问题,即验证最终唯一字段并阻止更新父序列化器。这个问题已经多次被问到以下问题:


  1. 对Django Rest Framework上的嵌套序列化程序进行唯一验证

  2. Django rest框架不使用FK创建对象到具有unique = True字段的模型

为简单起见,我们以以下示例为例第一个问题:

  class GenreSerializer(serializers.ModelSerializer):
class Meta:
fields =( 'name',)#此字段是唯一的
模型=类型
extra_kwargs = {
'name':{'validators':[]},
}

类BookSerializer(serializers.ModelSerializer):
ge nre = GenreSerializer()

class Meta:
模型=图书
字段=('name','genre')

def create(self ,validated_data):
#实现创建

def更新(自身,实例,validated_data):
#实现更新
  class BookViewSet(viewsets.ModelViewSet):
queryset = Book .objects.all()
序列化器= BookSerializer

def perform_create(self):
#实现逻辑并引发ValidationError

但是,这并不是很正确,因为我们正在验证流派在<$ c中的唯一性$ c> BookViewSet 。



另一种选择是在 create() BookSerializer 的$ c>方法,如第二个问题所述(请参见上面的列表)。



我真正想念的是什么在这两种解决方案中,验证错误未附加到模型 Genre 的字段 name 上,并且用户输入



我想将 Genre.name 的验证错误添加到现有的验证错误,请保留用户输入和确认



我的想法是这样的:

  class GenreSerializer(serializers.ModelSerializer):
#...
def validate_name(self,value):
#是否可以在此处检查是否已创建或更新?
if create:#这是逻辑
的占位符,如果self.Meta.model.objects.filter(name = value).exists():
提高ValidationError(' ')
返回值

#或重写__init__方法

def __init __(self,* args,** kwargs):
super(GenreSerializer,self).__ init __(* args,** kwargs)
#检查是否创建或更新
(如果创建):
self.fields ['name']。validators .append('验证逻辑')

这是否可行,或者是否有其他方法可以实现提到的目标-创建新实例时,保留用户输入并将验证错误附加到字段 name 到现有验证错误列表中吗?

解决方案

这是我的方法:

  class GenreSerializer(serializers.ModelSerializer):
#...剪切...
def validate_name(self,value):
if self.context ['request' ] ._ request.method =='POST':
如果self.Meta.model.objects.filter(name = value).exists():
引发ValidationError('此名称的流派已经存在。')
返回值

这样,仅当新的 Genre 对象创建( POST ),而不是在更新对象时( PUT )。

创建新的 Book 对象时,对类型的验证

验证后将保留所有表单输入,并且错误消息将附加到字段 name



这实际上满足了我的所有条件。尽管我不认为这是正确的做法。我仍然想知道如何手动调用 validate_name 中的 UniqueValidator ,而不是重新发明该验证。 / p>

编辑:



我找到了一种方法来调用 UniqueValidator 在方法中:

  def validate_name(self,value):
if self.context [' request'] ._ request.method =='POST':
unique = UniqueValidator(
self.Meta.model.objects.all(),
message ='此名称的流派已经存在。'

unique.set_context(self.fields ['name'])
unique(value)
返回值


When using writable nested serializers in DRF there is the known problem with validating eventual unique fields and preventing the updating of the parent serializer. This issue has been asked many times in questions like these:

  1. Unique validation on nested serializer on Django Rest Framework
  2. Django rest framework not creating object with FK to a model with unique=True field

For simplicity let's take the example from the first question:

class GenreSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ('name',) #This field is unique
        model = Genre
        extra_kwargs = {
            'name': {'validators': []},
        }

class BookSerializer(serializers.ModelSerializer):
    genre = GenreSerializer()

    class Meta:
        model = Book
        fields = ('name', 'genre')

    def create(self, validated_data):
        # implement creating

    def update(self, instance, validated_data):
        # implement updating

Now the problem is that the uniqueness validation is dropped out also for creating. This could be intercepted in the view, for example:

class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.all()
    serializer = BookSerializer

    def perform_create(self):
        # implement logic and raise ValidationError

However this doesn't feel really right because we're validating the uniqueness of Genre in BookViewSet.

The other option is to implement the validation in the create() method of BookSerializer as explained in the second question (see list above).

What I really miss in both solutions is that the validation error is not attached to the field name of the model Genre and the user input in the form is lost.

What I'd like is to add the validation error for Genre.name to the existing validation errors, keep the user input and do this only for creating, not for updating.

My ideas were something like this:

class GenreSerializer(serializers.ModelSerializer):
    # ...
    def validate_name(self, value):
        # is it possible to check here if it is create or update?
        if create: # this is a placeholder for the logic
             if self.Meta.model.objects.filter(name=value).exists():
                 raise ValidationError('A genre with this name already exists.')
        return value

    # or to override the __init__ method

    def __init__(self, *args, **kwargs):
        super(GenreSerializer, self).__init__(*args, **kwargs)
        # check if create or update
        if create:
            self.fields['name'].validators.append('validation logic')

Is this possible or is there any other way to achieve the before mentioned goal - keep user input and add validation error attached to the field name to the list of existing validation errors when creating new instance?

解决方案

This is how I did it:

class GenreSerializer(serializers.ModelSerializer):
    # ... snip ...
    def validate_name(self, value):
       if self.context['request']._request.method == 'POST':
           if self.Meta.model.objects.filter(name=value).exists():
               raise ValidationError('A genre with this name already exists.')
        return value

In this way the validation is triggered only when a new Genre object is created (POST), not when it is updated (PUT).
When a new Book object is created, the validation for Genre is propagated to the nested serializer.
All form inputs are preserved after validation and the error message is attached to the field name.

That actually fulfills all my criteria. Although I don't have the feeling that this is the right way of doing it. I'd still like to know how could I call manually the UniqueValidator in validate_name, instead of reinventing that validation.

EDIT:

I found out a way how to call the UniqueValidator in the method:

def validate_name(self, value):
    if self.context['request']._request.method == 'POST':
        unique = UniqueValidator(
            self.Meta.model.objects.all(),
            message='Genre with this name already exists.'
        )
        unique.set_context(self.fields['name'])
        unique(value)
    return value

这篇关于DRF:创建时验证嵌套的序列化器数据,但更新时不验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆