🐈

【Django REST framework】request.dataにはある値がvalidated_dataにはない時

2022/10/06に公開

作成したテストがなかなか通らなくて苦労したので下記にまとめます...

動作環境

Python 3.9.1
Django 3.1.7
DjangoRESTframework 3.12.2

エラー内容

UPDATEの処理で、request.bodyに入れた値をSerializerの中で取り出したいのに、空になってしまうエラーです。
Viewのrequest.dataには値が存在するのに、Serializerのvalidated_dataだと値が空になってしまいます。

・テストコード

    def test_update_foo(self):
        foo_pk = "75d00a80-1330-2517-fffb-62be168cf894"
        url = f"/api/foo/{foo_pk}/"
        user_pk = "42a57936ebec4de2a4e0be79ea10c003"
        user = User.objects.get(pk=user_pk)
        self.client.force_authenticate(user)

        data = {
            "foo_detail": "fooの詳細",
            "products": [
                {
                    "product_name": "商品の名前",
                    "price": 1000,
                }
            ],
        }
        res = self.client.patch(url, data)
        self.assertEqual(res.status_code, status.HTTP_200_OK)

・View

class FooViewSet(viewsets.ModelViewSet):
	queryset = Foo.objects.all()
	serializer_class = FooSerializer
	permission_classes = [IsAuthenticated, ]
    
	def update(self, request, *args, **kwargs):
	instance = self.get_object()
	serializer = self.get_serializer(
	    instance=instance,
	    context={'request': request, 'kwargs': self.kwargs},
	    data=request.data,
	    partial=True
	)
	serializer.is_valid(raise_exception=True)
	serializer.save()
	return Response(data=serializer.data, status=status.HTTP_200_OK)

・Serializer

	def update(self, instance, validated_data):
            products = None
            if 'products' in validated_data:
            products = validated_data.pop('products')
            # productsを処理するコード
	    
          instance = super().update(instance, validated_data)

          return instance

解決法

何でrequest.dataには入ってるのにvalidated_dataには値がないんだ!と混乱しましたが、
調べていくとテストコードでPOSTやUPDATEする際、request.bodyのvalueにリストがあると上記エラーになるようでした。
ですが、下記のように書くだけでこの問題は解決します。

・テストコード

        res = self.client.patch(url, data, format='json')

これでvalidated_dataでも無事に値を取得することができました!

考察

下記見るとテストのデフォルトのContent-Typeは、multipart/form-data のようです。
それをjsonに変えてあげればいいんですね。
ですが、テストではrequest headerの設定を変えられますが、本番のフロントの実装がどうなってるかはそれぞれのWebサービス次第だと思うので、そこは注意した方がいいかもですね。
https://www.django-rest-framework.org/api-guide/testing/#configuration

参考

https://stackoverflow.com/questions/65890892/django-field-not-appearing-in-validated-data-but-its-in-request

https://github.com/encode/django-rest-framework/issues/3884

https://www.django-rest-framework.org/tutorial/2-requests-and-responses/

Discussion