DRF:创建时验证嵌套的序列化器数据,但更新时不验证 [英] DRF: Validate nested serializer data when creating, but not when updating
问题描述
在DRF中使用可写的嵌套序列化器时,存在一个已知的问题,即验证最终唯一字段并阻止更新父序列化器。这个问题已经多次被问到以下问题:
为简单起见,我们以以下示例为例第一个问题:
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()$ c中实现验证
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:
- Unique validation on nested serializer on Django Rest Framework
- 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
inBookViewSet
.The other option is to implement the validation in the
create()
method ofBookSerializer
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 modelGenre
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 newBook
object is created, the validation forGenre
is propagated to the nested serializer.
All form inputs are preserved after validation and the error message is attached to the fieldname
.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
invalidate_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屋!