Django Rest Framework 中嵌套序列化程序的唯一验证
Unique validation on nested serializer on Django Rest Framework
我有一个这样的案例,您有一个带有唯一字段的自定义嵌套序列化程序关系。示例案例:
class GenreSerializer(serializers.ModelSerializer):
class Meta:
fields = ('name',) #This field is unique
model = Genre
class BookSerializer(serializers.ModelSerializer):
genre = GenreSerializer()
class Meta:
model = Book
fields = ('name', 'genre')
def create(self, validated_data):
genre = validated_data.pop('genre')
genre = Genre.objects.get(**genre)
return Book.objects.create(genre=genre, **validated_data)
问题: 当我尝试保存一个 json 对象时,如 {"name":"The Prince", "genre": {"name": "History"}} DRF 尝试验证流派对象的唯一约束,如果 "History" 存在则抛出异常,因为名称为流派"History" 必须是唯一的,这是真的,但我只是想关联对象而不是一起创建。
非常感谢!!
您应该删除嵌套序列化程序的唯一验证器:
class GenreSerializer(serializers.ModelSerializer):
class Meta:
fields = ('name',) #This field is unique
model = Genre
extra_kwargs = {
'name': {'validators': []},
}
您可能需要先打印您的序列化器,以确保您在该字段上没有其他验证器。如果您有一些,则必须将它们包含在列表中。
编辑:如果需要确保创建的唯一性约束,则应在调用 serializer.is_valid
之后和 serializer.save
.
之前在视图中进行
发生这种情况是因为嵌套序列化程序 (GenreSerializer
) 需要一个对象实例来正确验证唯一约束(例如将 exclude
子句放入验证时使用的查询集)并且默认情况下,序列化程序不会将相关对象的实例传递给运行 to_internal_value()
方法时嵌套序列化程序的文件。参见 here
另一种解决这个问题的方法是覆盖父序列化器的get_fields()
方法并传递相关对象的实例
class BookSerializer(serializers.ModelSerializer):
def get_fields(self):
fields = super(BookSerializer, self).get_fields()
try: # Handle DoesNotExist exceptions (you may need it)
if self.instance and self.instance.genre:
fields['genre'].instance = self.instance.genre
except Genre.DoesNotExist:
pass
return fields
一起删除 UniqueValidator
使用
'name': {'validators': []}
您需要自己验证 Unique 条目而忽略当前对象,因为当其他人尝试保存相同名称时不会出现 500 错误,像这样的方法会起作用:
def validate_name(self, value):
check_query = Genre.objects.filter(name=value)
if self.instance:
check_query = check_query.exclude(pk=self.instance.pk)
if self.parent is not None and self.parent.instance is not None:
genre = getattr(self.parent.instance, self.field_name)
check_query = check_query.exclude(pk=genre.pk)
if check_query.exists():
raise serializers.ValidationError('A Genre with this name already exists
.')
return value
调用方法 validate_<field>
来验证您的所有字段,请参阅 docs。
我有一个这样的案例,您有一个带有唯一字段的自定义嵌套序列化程序关系。示例案例:
class GenreSerializer(serializers.ModelSerializer):
class Meta:
fields = ('name',) #This field is unique
model = Genre
class BookSerializer(serializers.ModelSerializer):
genre = GenreSerializer()
class Meta:
model = Book
fields = ('name', 'genre')
def create(self, validated_data):
genre = validated_data.pop('genre')
genre = Genre.objects.get(**genre)
return Book.objects.create(genre=genre, **validated_data)
问题: 当我尝试保存一个 json 对象时,如 {"name":"The Prince", "genre": {"name": "History"}} DRF 尝试验证流派对象的唯一约束,如果 "History" 存在则抛出异常,因为名称为流派"History" 必须是唯一的,这是真的,但我只是想关联对象而不是一起创建。
非常感谢!!
您应该删除嵌套序列化程序的唯一验证器:
class GenreSerializer(serializers.ModelSerializer):
class Meta:
fields = ('name',) #This field is unique
model = Genre
extra_kwargs = {
'name': {'validators': []},
}
您可能需要先打印您的序列化器,以确保您在该字段上没有其他验证器。如果您有一些,则必须将它们包含在列表中。
编辑:如果需要确保创建的唯一性约束,则应在调用 serializer.is_valid
之后和 serializer.save
.
发生这种情况是因为嵌套序列化程序 (GenreSerializer
) 需要一个对象实例来正确验证唯一约束(例如将 exclude
子句放入验证时使用的查询集)并且默认情况下,序列化程序不会将相关对象的实例传递给运行 to_internal_value()
方法时嵌套序列化程序的文件。参见 here
另一种解决这个问题的方法是覆盖父序列化器的get_fields()
方法并传递相关对象的实例
class BookSerializer(serializers.ModelSerializer):
def get_fields(self):
fields = super(BookSerializer, self).get_fields()
try: # Handle DoesNotExist exceptions (you may need it)
if self.instance and self.instance.genre:
fields['genre'].instance = self.instance.genre
except Genre.DoesNotExist:
pass
return fields
一起删除 UniqueValidator
使用
'name': {'validators': []}
您需要自己验证 Unique 条目而忽略当前对象,因为当其他人尝试保存相同名称时不会出现 500 错误,像这样的方法会起作用:
def validate_name(self, value):
check_query = Genre.objects.filter(name=value)
if self.instance:
check_query = check_query.exclude(pk=self.instance.pk)
if self.parent is not None and self.parent.instance is not None:
genre = getattr(self.parent.instance, self.field_name)
check_query = check_query.exclude(pk=genre.pk)
if check_query.exists():
raise serializers.ValidationError('A Genre with this name already exists
.')
return value
调用方法 validate_<field>
来验证您的所有字段,请参阅 docs。