Fix nested write of non-relational fields (#6916)

This commit is contained in:
Konstantinos Tselepakis 2019-10-03 19:33:37 +03:00 committed by Ryan P Kilby
parent 0dac98d215
commit 30e56f62ba
2 changed files with 62 additions and 0 deletions

View File

@ -790,6 +790,8 @@ def raise_errors_on_nested_writes(method_name, serializer, validated_data):
* Silently ignore the nested part of the update.
* Automatically create a profile instance.
"""
ModelClass = serializer.Meta.model
model_field_info = model_meta.get_field_info(ModelClass)
# Ensure we don't have a writable nested field. For example:
#
@ -799,6 +801,7 @@ def raise_errors_on_nested_writes(method_name, serializer, validated_data):
assert not any(
isinstance(field, BaseSerializer) and
(field.source in validated_data) and
(field.source in model_field_info.relations) and
isinstance(validated_data[field.source], (list, dict))
for field in serializer._writable_fields
), (
@ -817,9 +820,19 @@ def raise_errors_on_nested_writes(method_name, serializer, validated_data):
# class UserSerializer(ModelSerializer):
# ...
# address = serializer.CharField('profile.address')
#
# Though, non-relational fields (e.g., JSONField) are acceptable. For example:
#
# class NonRelationalPersonModel(models.Model):
# profile = JSONField()
#
# class UserSerializer(ModelSerializer):
# ...
# address = serializer.CharField('profile.address')
assert not any(
len(field.source_attrs) > 1 and
(field.source_attrs[0] in validated_data) and
(field.source_attrs[0] in model_field_info.relations) and
isinstance(validated_data[field.source_attrs[0]], (list, dict))
for field in serializer._writable_fields
), (

View File

@ -4,6 +4,8 @@ from django.http import QueryDict
from django.test import TestCase
from rest_framework import serializers
from rest_framework.compat import postgres_fields
from rest_framework.serializers import raise_errors_on_nested_writes
class TestNestedSerializer:
@ -302,3 +304,50 @@ class TestNestedWriteErrors(TestCase):
'serializer `tests.test_serializer_nested.DottedAddressSerializer`, '
'or set `read_only=True` on dotted-source serializer fields.'
)
if postgres_fields:
class NonRelationalPersonModel(models.Model):
"""Model declaring a postgres JSONField"""
data = postgres_fields.JSONField()
@pytest.mark.skipif(not postgres_fields, reason='psycopg2 is not installed')
class TestNestedNonRelationalFieldWrite:
"""
Test that raise_errors_on_nested_writes does not raise `AssertionError` when the
model field is not a relation.
"""
def test_nested_serializer_create_and_update(self):
class NonRelationalPersonDataSerializer(serializers.Serializer):
occupation = serializers.CharField()
class NonRelationalPersonSerializer(serializers.ModelSerializer):
data = NonRelationalPersonDataSerializer()
class Meta:
model = NonRelationalPersonModel
fields = ['data']
serializer = NonRelationalPersonSerializer(data={'data': {'occupation': 'developer'}})
assert serializer.is_valid()
assert serializer.validated_data == {'data': {'occupation': 'developer'}}
raise_errors_on_nested_writes('create', serializer, serializer.validated_data)
raise_errors_on_nested_writes('update', serializer, serializer.validated_data)
def test_dotted_source_field_create_and_update(self):
class DottedNonRelationalPersonSerializer(serializers.ModelSerializer):
occupation = serializers.CharField(source='data.occupation')
class Meta:
model = NonRelationalPersonModel
fields = ['occupation']
serializer = DottedNonRelationalPersonSerializer(data={'occupation': 'developer'})
assert serializer.is_valid()
assert serializer.validated_data == {'data': {'occupation': 'developer'}}
raise_errors_on_nested_writes('create', serializer, serializer.validated_data)
raise_errors_on_nested_writes('update', serializer, serializer.validated_data)