web-dev-qa-db-ja.com

Django RestFrameworkのネストされたシリアライザーでの一意の検証

このような場合、一意のフィールドを持つカスタムのネストされたシリアライザー関係があります。サンプルケース:

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)

問題: {"name": "The Prince"、 "genre":{"name": "History"}}のようなjsonオブジェクトを保存しようとすると、DRFはジャンルオブジェクトを検証しようとします一意の制約と "History"が存在する場合、名前 "History"のジャンルは一意である必要があるため、例外がスローされます。 trueですが、オブジェクトを関連付けようとしているだけで、一緒に作成することはありません。

本当にありがとうございます!!

13

ネストされたシリアライザーの一意のバリデーターを削除する必要があります。

class GenreSerializer(serializers.ModelSerializer):

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

そのフィールドに他のバリデーターがないことを確認する前に、シリアライザーを印刷することをお勧めします。いくつかある場合は、リストに含める必要があります。

編集:作成時に一意性の制約を確認する必要がある場合は、serializer.is_validが呼び出された後、serializer.saveの前にビューで行う必要があります。

18
Linovia

これは、ネストされたシリアライザー(GenreSerializer)が一意の制約を正しく検証するためにオブジェクトのインスタンスを必要とし(検証で使用されるクエリセットにexclude句を配置するなど)、デフォルトではシリアライザーが必要になるために発生しますto_internal_value()メソッドを実行すると、関連オブジェクトのインスタンスがネストされたシリアライザーであるファイルに渡されません。 ここ を参照してください

この問題を解決する別の方法は、親シリアライザーの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
1
Lucas Weyne

一緒に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 を参照してください。

1
Lucas Paim