mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-02-16 19:41:06 +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