mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 09:57:55 +03:00 
			
		
		
		
	Fix nested write of non-relational fields (#6916)
This commit is contained in:
		
							parent
							
								
									0dac98d215
								
							
						
					
					
						commit
						30e56f62ba
					
				| 
						 | 
					@ -790,6 +790,8 @@ def raise_errors_on_nested_writes(method_name, serializer, validated_data):
 | 
				
			||||||
    * Silently ignore the nested part of the update.
 | 
					    * Silently ignore the nested part of the update.
 | 
				
			||||||
    * Automatically create a profile instance.
 | 
					    * 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:
 | 
					    # 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(
 | 
					    assert not any(
 | 
				
			||||||
        isinstance(field, BaseSerializer) and
 | 
					        isinstance(field, BaseSerializer) and
 | 
				
			||||||
        (field.source in validated_data) and
 | 
					        (field.source in validated_data) and
 | 
				
			||||||
 | 
					        (field.source in model_field_info.relations) and
 | 
				
			||||||
        isinstance(validated_data[field.source], (list, dict))
 | 
					        isinstance(validated_data[field.source], (list, dict))
 | 
				
			||||||
        for field in serializer._writable_fields
 | 
					        for field in serializer._writable_fields
 | 
				
			||||||
    ), (
 | 
					    ), (
 | 
				
			||||||
| 
						 | 
					@ -817,9 +820,19 @@ def raise_errors_on_nested_writes(method_name, serializer, validated_data):
 | 
				
			||||||
    # class UserSerializer(ModelSerializer):
 | 
					    # class UserSerializer(ModelSerializer):
 | 
				
			||||||
    #     ...
 | 
					    #     ...
 | 
				
			||||||
    #     address = serializer.CharField('profile.address')
 | 
					    #     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(
 | 
					    assert not any(
 | 
				
			||||||
        len(field.source_attrs) > 1 and
 | 
					        len(field.source_attrs) > 1 and
 | 
				
			||||||
        (field.source_attrs[0] in validated_data) 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))
 | 
					        isinstance(validated_data[field.source_attrs[0]], (list, dict))
 | 
				
			||||||
        for field in serializer._writable_fields
 | 
					        for field in serializer._writable_fields
 | 
				
			||||||
    ), (
 | 
					    ), (
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,8 @@ from django.http import QueryDict
 | 
				
			||||||
from django.test import TestCase
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from rest_framework import serializers
 | 
					from rest_framework import serializers
 | 
				
			||||||
 | 
					from rest_framework.compat import postgres_fields
 | 
				
			||||||
 | 
					from rest_framework.serializers import raise_errors_on_nested_writes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestNestedSerializer:
 | 
					class TestNestedSerializer:
 | 
				
			||||||
| 
						 | 
					@ -302,3 +304,50 @@ class TestNestedWriteErrors(TestCase):
 | 
				
			||||||
            'serializer `tests.test_serializer_nested.DottedAddressSerializer`, '
 | 
					            'serializer `tests.test_serializer_nested.DottedAddressSerializer`, '
 | 
				
			||||||
            'or set `read_only=True` on dotted-source serializer fields.'
 | 
					            '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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user