关于Django Rest Framework上嵌套序列化器的唯一验证

时间:2016-07-18 13:36:43

标签: python django rest django-rest-framework django-serializer

我有一个这样的情况,你有一个与唯一字段的自定义嵌套序列化器关系。示例案例:

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”的类型必须是唯一的,这是真的,但我只想尝试关联对象而不是一起创造。

非常感谢!!

3 个答案:

答案 0 :(得分:14)

您应该删除嵌套序列化程序的唯一验证器:

class GenreSerializer(serializers.ModelSerializer):

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

您可能需要先打印序列化程序,以确保该字段上没有其他验证程序。如果你有一些,你必须将它们包括在列表中。

编辑:如果您需要确保创建的唯一性约束,则应在调用serializer.is_valid之后和serializer.save之前在视图中执行此操作。

答案 1 :(得分:0)

之所以会这样,是因为嵌套的序列化程序(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

答案 2 :(得分:0)

一起使用{p>删除UniqueValidator

'name': {'validators': []}

您需要自己验证唯一项,而忽略当前对象,否则当另一个人尝试保存相同名称时不会出现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