DjangoRestFrameWork ネストしたフィールドにユニークキーが含まれる時、updateで一意制約違反
先に解決法だけ見たい人は
class MemberSerializer(serializers.ModelSerializer): """各メンバ-エリア""" class Meta: model = Member fields = ('id', 'name', 'birthday', 'coler') # ユニークキーの入力チェックを外す extra_kwargs = { 'name': {'validators': []}, }
やりたかったこと
例えば、アイドルの名前は、エゴサ、パブサで検知しやすいように、全作品でユニークになっていなければならないとします。 で、グループ名や結成時期とメンバーの情報をapi一本で更新したいとするじゃないですか。 そうするとシリアライザーは多分こんな感じ。
selializer.py
class MemberSerializer(serializers.ModelSerializer): """各メンバーエリア""" class Meta: model = Member fields = ('id', 'name', 'birthday', 'coler') class UnderGroundIdleSerializer(serializers.ModelSerializer): """アイドルのグループ名""" members = MemberSerializer(many=True, source='members', required=False) class Meta: model = UnderGroudIdle fields = ('id', 'name', 'members')
View.py
class IdleViewSet(viewsets.ModelViewSet): pagination_class = PostPagination queryset = UnderGroundIdle.objects.all().prefetch_related('members') serializer_class = UnderGroundIdleSerializer parser_classes = (MultipartFormencodeParser, JSONParser,) @transaction.atomic def update(self, request, *args, **kwargs): """特に意味はないけど分かりやすく可視化""" responce = super().update(request, *args, **kwargs) ★ここが重要になってきます return responce
postする時のjsonはこんな感じ(他のメンバーごめんなさい)
{ "name": "Aqours", "members": [ { "birthday": "3-4", "coler": "Yellow", "name": "国木田花丸" }, { "birthday": "4-17", "coler": "Blue", "name": "渡辺曜" } ] }
なんですが、これをこのままputすると、一意制約違反エラーとなってしまいます。(国木田花丸と渡辺曜がすでに存在するというエラー)
原因
Djangoの入力値のチェックで何故かinsert時と同じチェックをしている。。
responce = super().update(request, *args, **kwargs) ★ここから呼ばれるのが以下のクラス class UpdateModelMixin(object): """ Update a model instance. """ def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) instance = self.get_object() serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) ★ここでエラーが起きる self.perform_update(serializer) if getattr(instance, '_prefetched_objects_cache', None): # If 'prefetch_related' has been applied to a queryset, we need to # forcibly invalidate the prefetch cache on the instance. instance._prefetched_objects_cache = {} return Response(serializer.data)
対策
シリアライザーでチェック処理を外す指定ができる。
class MemberSerializer(serializers.ModelSerializer): """各メンバ-エリア""" class Meta: model = Member fields = ('id', 'name', 'birthday', 'coler') # この2行を追加 extra_kwargs = { 'name': {'validators': []}, }
こうするとチェック処理が動かなくなるので自前でvalidateを書くなどの処理でupdateが出来るようにしました。 もちろんDjangoでチェックしなくてもデータベースから一意制約違反のエラーは出てくるので結果的には一意制約を保つことができます。(なんか嫌ですが)
ネストしたモデルシリアライザーって結局自前でupdateしなきゃ行けないのにチェック処理は全部クリエイト基準で行われるってなんか微妙な感じ。。