Django DRF, не удается воспроизвести POST-запрос с использованием ApiClient и APITestCase, поля полезной нагрузки удалены
У меня есть следующие настройки в Django DRF:
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):
tags = validated_data.pop("tags", [])
failure_mode = self.Meta.model.objects.create(**validated_data)
for tag in tags:
current_tag, _ = models.Tag.objects.get_or_create(**tag)
failure_mode.tags.add(current_tag)
return failure_mode
class ItemSerializer(TagCreationMixin, serializers.ModelSerializer):
tags = TagSerializer(many=True, required=False)
class Meta:
model = models.Item
fields = "__all__"
И я хочу автоматизировать тесты, используя APIClient и APITestCase. У меня определен следующий тест:
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())
Который возвращает значение 201, но без каких-либо тегов, похоже, что они извлечены из начальной полезной нагрузки:
<Response status_code=201, "application/json">
{'id': 'f3cd6bbb-239f-4f47-9b2d-f648ead76bdd', 'tags': [], 'name': 'test'}
В любом случае, когда я бросаю вызов Swagger с той же конечной точкой и полезной нагрузкой, это работает так, как ожидалось:
{
"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"
}
Почему я не могу воспроизвести то же поведение, что и Swagger, используя APIClient.
У меня наконец-то появилось время протестировать решение. Я думаю, мы можем это исправить, используя .to_internal_value() вместо:
class TagSerializer(serializers.Serializer):
id = serializers.UUIDField(read_only=True)
name = serializers.CharField(required=True)
style = serializers.JSONField(read_only=True)
def to_internal_value(self, validated_data):
data = dict(validated_data)
name = data.pop('name')
tag, __ = Tag.objects.get_or_create(name=name, defaults=data)
return tag
class TagCreationMixin:
def create(self, validated_data):
data = dict(validated_data)
tags = data.pop('tags', [])
item = self.Meta.model.objects.create(**data)
book.tags.add(*tags)
return item
class ItemSerializer(TagCreationMixin, serializers.ModelSerializer):
tags = TagSerializer(many=True, required=False)
class Meta:
model = models.Item
fields = '__all__'
Вероятно, также лучше работать с неполными копиями проверенных данных, поскольку REST-фреймворк Django не создает копию самих данных, что может привести к неожиданному поведению.