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.
 | 
			
		||||
    * 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
 | 
			
		||||
    ), (
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user