使用APIClient和APITestCase无法复现POST请求:Payload字段丢失
问题:APIClient测试时标签未被正确关联,Swagger调用正常
场景复现
序列化器配置
class TagSerializer(serializers.Serializer): id = serializers.UUIDField(read_only=True) name = serializers.CharField(required=True) style = serializers.JSONField(read_only=True) class TagCreationMixin: def create(self, validated_data): data = copy.deepcopy(validated_data) tags = data.pop("tags", []) item = self.Meta.model.objects.create(**data) for tag in tags: current_tag, _ = models.Tag.objects.get_or_create(**tag) item.tags.add(current_tag) return item class ItemSerializer(TagCreationMixin, serializers.ModelSerializer): tags = TagSerializer(many=True, required=False) class Meta: model = models.Item fields = "__all__"
测试用例代码
class TestAPIMissingTags(APITestCase): fixtures = [ "core/fixtures/users.yaml" ] payload = { "name": "test", "tags": [{"name": "test"}, {"name": "critical"}], } def setUp(self): self.user = models.CustomUser.objects.get(username="jlandercy") self.client = APIClient() self.client.force_authenticate(user=self.user) def test_complete_payload_is_sent(self): response = self.client.post("/api/core/item/", data=self.payload) print(response) print(response.json())
现象
- APIClient测试返回201,但响应中
tags为空数组:<Response status_code=201, "application/json"> {'id': 'f3cd6bbb-239f-4f47-9b2d-f648ead76bdd', 'tags': [], 'name': 'test'} - 用Swagger调用相同接口和Payload,响应包含正确关联的标签:
{ "id": "6d05c2e3-fce3-420d-bef9-4bccd28062f7", "tags": [ { "id": "fa70c875-818b-4ca2-9417-4209fd377453", "name": "test", "style": { "background-color": "#cc7a13" } }, { "id": "9fd59657-4242-432f-b165-1ed0a67546e3", "name": "critical", "style": { "background-color": "#bf8100" } } ], "name": "test" }
原因分析
APIClient的post方法中,使用data参数时默认会把数据编码为表单格式(application/x-www-form-urlencoded),这种格式无法正确传递嵌套的复杂结构(比如包含字典的列表)。而Swagger发送的是JSON格式请求(Content-Type: application/json),DRF能正确解析嵌套数据。
当用data传递嵌套结构时,DRF的解析器无法识别tags字段的嵌套内容,导致validated_data中没有tags键,TagCreationMixin的create方法只能拿到空列表,最终没有关联任何标签。
解决方法
将post方法中的data参数替换为json参数,让APIClient发送JSON格式的请求:
def test_complete_payload_is_sent(self): response = self.client.post("/api/core/item/", json=self.payload) print(response) print(response.json())
这样请求会以application/json格式发送,DRF能正确解析tags字段的嵌套结构,validated_data中会包含完整的标签数据,create方法就能正常创建并关联标签,响应也会返回正确的标签列表。
内容的提问来源于stack exchange,提问作者jlandercy
相关产品推荐
相关产品推荐

