【Django REST framework】request.dataにはある値がvalidated_dataにはない時
作成したテストがなかなか通らなくて苦労したので下記にまとめます...
動作環境
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://github.com/encode/django-rest-framework/issues/3884
https://www.django-rest-framework.org/tutorial/2-requests-and-responses/
Discussion