From a717d74f3cc6c9526346c89f6828ceec493f725b Mon Sep 17 00:00:00 2001 From: Saadullah Aleem Date: Thu, 11 May 2023 01:19:12 +0500 Subject: [PATCH 01/19] fix: Make the instance variable of child serializer point to the correct list object instead of the entire list when validating ListSerializer --- rest_framework/serializers.py | 10 +++++- tests/test_serializer.py | 63 +++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 01bebf5fca..fb2a5bd1eb 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -589,6 +589,11 @@ def __init__(self, *args, **kwargs): self.min_length = kwargs.pop('min_length', None) assert self.child is not None, '`child` is a required argument.' assert not inspect.isclass(self.child), '`child` has not been instantiated.' + + if kwargs.get('instance', []) and kwargs.get('data', []): + assert len(kwargs.get("data", [])) == len( + kwargs.get("instance", [])), 'Data and instance should have same length' + super().__init__(*args, **kwargs) self.child.bind(field_name='', parent=self) @@ -663,7 +668,10 @@ def to_internal_value(self, data): ret = [] errors = [] - for item in data: + for idx, item in enumerate(data): + if hasattr(self, 'instance') and self.instance and \ + len(self.instance) > idx: + self.child.instance = self.instance[idx] try: validated = self.child.run_validation(item) except ValidationError as exc: diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 1d9efaa434..0f57ba6d28 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -762,3 +762,66 @@ class TestSerializer(serializers.Serializer): assert (s.data | {}).__class__ == s.data.__class__ assert ({} | s.data).__class__ == s.data.__class__ + + +class MyClass(models.Model): + name = models.CharField(max_length=100) + value = models.CharField(max_length=100, blank=True) + + app_label = "test" + + @property + def is_valid(self): + return self.name == 'valid' + + +class MyClassSerializer(serializers.ModelSerializer): + class Meta: + model = MyClass + fields = ('id', 'name', 'value') + + def validate_value(self, value): + if value and not self.instance.is_valid: + raise serializers.ValidationError( + 'Status cannot be set for invalid instance') + return value + + +import unittest + + +class TestMultipleObjectsValidation(unittest.TestCase): + def setUp(self): + self.objs = [ + MyClass(name='valid'), + MyClass(name='invalid'), + MyClass(name='other'), + ] + + def test_multiple_objects_are_validated_separately(self): + + serializer = MyClassSerializer( + data=[{'value': 'set', 'id': instance.id} for instance in + self.objs], + instance=self.objs, + many=True, + partial=True, + ) + + assert not serializer.is_valid() + assert serializer.errors == [ + {}, + {'value': ['Status cannot be set for invalid instance']}, + {'value': ['Status cannot be set for invalid instance']} + ] + + def test_exception_raised_when_data_and_instance_length_different(self): + + with self.assertRaises(AssertionError): + serializer = MyClassSerializer( + data=[{'value': 'set', 'id': instance.id} for instance in + self.objs], + instance=self.objs[:-1], + many=True, + partial=True, + ) \ No newline at end of file From 4bbc689ec5b442b2424086b21d1aed352bc96e8b Mon Sep 17 00:00:00 2001 From: Saadullah Aleem Date: Thu, 11 May 2023 01:33:19 +0500 Subject: [PATCH 02/19] fix formatting issues for list serializer validation fix --- rest_framework/serializers.py | 2 +- tests/test_serializer.py | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index fb2a5bd1eb..9301484dc2 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -592,7 +592,7 @@ def __init__(self, *args, **kwargs): if kwargs.get('instance', []) and kwargs.get('data', []): assert len(kwargs.get("data", [])) == len( - kwargs.get("instance", [])), 'Data and instance should have same length' + kwargs.get("instance", [])), 'Data and instance should have same length' super().__init__(*args, **kwargs) self.child.bind(field_name='', parent=self) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 0f57ba6d28..cfa2e838cc 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -2,6 +2,7 @@ import pickle import re import sys +import unittest from collections import ChainMap from collections.abc import Mapping @@ -10,7 +11,6 @@ from rest_framework import exceptions, fields, relations, serializers from rest_framework.fields import Field - from .models import ( ForeignKeyTarget, NestedForeignKeySource, NullableForeignKeySource ) @@ -787,9 +787,6 @@ def validate_value(self, value): return value -import unittest - - class TestMultipleObjectsValidation(unittest.TestCase): def setUp(self): self.objs = [ @@ -818,10 +815,10 @@ def test_multiple_objects_are_validated_separately(self): def test_exception_raised_when_data_and_instance_length_different(self): with self.assertRaises(AssertionError): - serializer = MyClassSerializer( + MyClassSerializer( data=[{'value': 'set', 'id': instance.id} for instance in self.objs], instance=self.objs[:-1], many=True, partial=True, - ) \ No newline at end of file + ) From 516ba527dd181035049c9b7ecef9bd618da07376 Mon Sep 17 00:00:00 2001 From: Saadullah Aleem Date: Thu, 11 May 2023 01:36:00 +0500 Subject: [PATCH 03/19] fix imports sorting for list serializer tests --- tests/test_serializer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index cfa2e838cc..9e286babd5 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -11,6 +11,7 @@ from rest_framework import exceptions, fields, relations, serializers from rest_framework.fields import Field + from .models import ( ForeignKeyTarget, NestedForeignKeySource, NullableForeignKeySource ) From b9c8d1923d0f017bd6b59efff5a9b3619067dd76 Mon Sep 17 00:00:00 2001 From: Mathieu Dupuy Date: Sun, 14 May 2023 02:00:13 +0200 Subject: [PATCH 04/19] remove django 2.2 from docs index (#8982) --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index cab5511ac1..ad241c0a37 100644 --- a/docs/index.md +++ b/docs/index.md @@ -86,7 +86,7 @@ continued development by **[signing up for a paid plan][funding]**. REST framework requires the following: * Python (3.6, 3.7, 3.8, 3.9, 3.10, 3.11) -* Django (2.2, 3.0, 3.1, 3.2, 4.0, 4.1) +* Django (3.0, 3.1, 3.2, 4.0, 4.1) We **highly recommend** and only officially support the latest patch release of each Python and Django series. From e6655e3b97ed7d1849a8a6077cf575cdc1d6efef Mon Sep 17 00:00:00 2001 From: Mehraz Hossain Rumman <59512321+MehrazRumman@users.noreply.github.com> Date: Mon, 15 May 2023 21:02:17 +0600 Subject: [PATCH 05/19] Declared Django 4.2 support in README.md (#8985) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e8375291dc..c2975a418f 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ There is a live example API for testing purposes, [available here][sandbox]. # Requirements * Python 3.6+ -* Django 4.1, 4.0, 3.2, 3.1, 3.0 +* Django 4.2, 4.1, 4.0, 3.2, 3.1, 3.0 We **highly recommend** and only officially support the latest patch release of each Python and Django series. From 01b99ddb9e5f1dd31c980bf57937dc3d689b3cdf Mon Sep 17 00:00:00 2001 From: Dominik Bruhn Date: Wed, 17 May 2023 07:46:48 +0200 Subject: [PATCH 06/19] Fix Links in Documentation to Django `reverse` and `reverse_lazy` (#8986) * Fix Django Docs url in reverse.md Django URLs of the documentation of `reverse` and `reverse_lazy` were wrong. * Update reverse.md --- docs/api-guide/reverse.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md index 82dd168818..c3fa52f21f 100644 --- a/docs/api-guide/reverse.md +++ b/docs/api-guide/reverse.md @@ -54,5 +54,5 @@ As with the `reverse` function, you should **include the request as a keyword ar api_root = reverse_lazy('api-root', request=request) [cite]: https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5 -[reverse]: https://docs.djangoproject.com/en/stable/topics/http/urls/#reverse -[reverse-lazy]: https://docs.djangoproject.com/en/stable/topics/http/urls/#reverse-lazy +[reverse]: https://docs.djangoproject.com/en/stable/ref/urlresolvers/#reverse +[reverse-lazy]: https://docs.djangoproject.com/en/stable/ref/urlresolvers/#reverse-lazy From 8d7e250046781a32635ed217e07afedfb00ddcb8 Mon Sep 17 00:00:00 2001 From: jornvanwier Date: Thu, 18 May 2023 05:46:40 +0200 Subject: [PATCH 07/19] fix URLPathVersioning reverse fallback (#7247) * fix URLPathVersioning reverse fallback * add test for URLPathVersioning reverse fallback * Update tests/test_versioning.py --------- Co-authored-by: Jorn van Wier Co-authored-by: Asif Saif Uddin --- rest_framework/versioning.py | 6 ++++-- tests/test_versioning.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/rest_framework/versioning.py b/rest_framework/versioning.py index 78cfc9dc81..c2764c7a40 100644 --- a/rest_framework/versioning.py +++ b/rest_framework/versioning.py @@ -81,8 +81,10 @@ def determine_version(self, request, *args, **kwargs): def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): if request.version is not None: - kwargs = {} if (kwargs is None) else kwargs - kwargs[self.version_param] = request.version + kwargs = { + self.version_param: request.version, + **(kwargs or {}) + } return super().reverse( viewname, args, kwargs, request, format, **extra diff --git a/tests/test_versioning.py b/tests/test_versioning.py index 93f61d2be4..b216461840 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -152,6 +152,8 @@ class TestURLReversing(URLPatternsTestCase, APITestCase): path('v1/', include((included, 'v1'), namespace='v1')), path('another/', dummy_view, name='another'), re_path(r'^(?P[v1|v2]+)/another/$', dummy_view, name='another'), + re_path(r'^(?P.+)/unversioned/$', dummy_view, name='unversioned'), + ] def test_reverse_unversioned(self): @@ -198,6 +200,14 @@ def test_reverse_url_path_versioning(self): response = view(request) assert response.data == {'url': 'http://testserver/another/'} + # Test fallback when kwargs is not None + request = factory.get('/v1/endpoint/') + request.versioning_scheme = scheme() + request.version = 'v1' + + reversed_url = reverse('unversioned', request=request, kwargs={'foo': 'bar'}) + assert reversed_url == 'http://testserver/bar/unversioned/' + def test_reverse_namespace_versioning(self): class FakeResolverMatch(ResolverMatch): namespace = 'v1' From b9b50bd0cc8b47797ccce9ad71199424993b7110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Beaul=C3=A9?= Date: Wed, 24 May 2023 10:59:42 -0300 Subject: [PATCH 08/19] Make set_value a method within `Serializer` (#8001) * Make set_value a static method for Serializers As an alternative to #7671, let the method be overridden if needed. As the function is only used for serializers, it has a better place in the Serializer class. * Set `set_value` as an object (non-static) method * Add tests for set_value() These tests follow the examples given in the method. --- rest_framework/fields.py | 21 --------------------- rest_framework/serializers.py | 24 ++++++++++++++++++++++-- tests/test_serializer.py | 21 +++++++++++++++++++++ 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 4f82e4a10e..623e72e0ad 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -113,27 +113,6 @@ def get_attribute(instance, attrs): return instance -def set_value(dictionary, keys, value): - """ - Similar to Python's built in `dictionary[key] = value`, - but takes a list of nested keys instead of a single key. - - set_value({'a': 1}, [], {'b': 2}) -> {'a': 1, 'b': 2} - set_value({'a': 1}, ['x'], 2) -> {'a': 1, 'x': 2} - set_value({'a': 1}, ['x', 'y'], 2) -> {'a': 1, 'x': {'y': 2}} - """ - if not keys: - dictionary.update(value) - return - - for key in keys[:-1]: - if key not in dictionary: - dictionary[key] = {} - dictionary = dictionary[key] - - dictionary[keys[-1]] = value - - def to_choices_dict(choices): """ Convert choices into key/value dicts. diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 9301484dc2..12723c9dcd 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -28,7 +28,7 @@ from rest_framework.compat import postgres_fields from rest_framework.exceptions import ErrorDetail, ValidationError -from rest_framework.fields import get_error_detail, set_value +from rest_framework.fields import get_error_detail from rest_framework.settings import api_settings from rest_framework.utils import html, model_meta, representation from rest_framework.utils.field_mapping import ( @@ -346,6 +346,26 @@ class Serializer(BaseSerializer, metaclass=SerializerMetaclass): 'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.') } + def set_value(self, dictionary, keys, value): + """ + Similar to Python's built in `dictionary[key] = value`, + but takes a list of nested keys instead of a single key. + + set_value({'a': 1}, [], {'b': 2}) -> {'a': 1, 'b': 2} + set_value({'a': 1}, ['x'], 2) -> {'a': 1, 'x': 2} + set_value({'a': 1}, ['x', 'y'], 2) -> {'a': 1, 'x': {'y': 2}} + """ + if not keys: + dictionary.update(value) + return + + for key in keys[:-1]: + if key not in dictionary: + dictionary[key] = {} + dictionary = dictionary[key] + + dictionary[keys[-1]] = value + @cached_property def fields(self): """ @@ -492,7 +512,7 @@ def to_internal_value(self, data): except SkipField: pass else: - set_value(ret, field.source_attrs, validated_value) + self.set_value(ret, field.source_attrs, validated_value) if errors: raise ValidationError(errors) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 9e286babd5..39d9238ef9 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -765,6 +765,27 @@ class TestSerializer(serializers.Serializer): assert ({} | s.data).__class__ == s.data.__class__ +class TestSetValueMethod: + # Serializer.set_value() modifies the first parameter in-place. + + s = serializers.Serializer() + + def test_no_keys(self): + ret = {'a': 1} + self.s.set_value(ret, [], {'b': 2}) + assert ret == {'a': 1, 'b': 2} + + def test_one_key(self): + ret = {'a': 1} + self.s.set_value(ret, ['x'], 2) + assert ret == {'a': 1, 'x': 2} + + def test_nested_key(self): + ret = {'a': 1} + self.s.set_value(ret, ['x', 'y'], 2) + assert ret == {'a': 1, 'x': {'y': 2}} + + class MyClass(models.Model): name = models.CharField(max_length=100) value = models.CharField(max_length=100, blank=True) From 869d46fc4294f5a30bdac1b0ffe2ef5e2c7fe21e Mon Sep 17 00:00:00 2001 From: Saadullah Aleem Date: Thu, 11 May 2023 01:19:12 +0500 Subject: [PATCH 09/19] fix: Make the instance variable of child serializer point to the correct list object instead of the entire list when validating ListSerializer --- tests/test_serializer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 39d9238ef9..9979c8e20b 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -837,10 +837,10 @@ def test_multiple_objects_are_validated_separately(self): def test_exception_raised_when_data_and_instance_length_different(self): with self.assertRaises(AssertionError): - MyClassSerializer( + serializer = MyClassSerializer( data=[{'value': 'set', 'id': instance.id} for instance in self.objs], instance=self.objs[:-1], many=True, partial=True, - ) + ) \ No newline at end of file From f969143c950e79ea6c71412b259118a888f1e1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Beaul=C3=A9?= Date: Wed, 24 May 2023 10:59:42 -0300 Subject: [PATCH 10/19] Make set_value a method within `Serializer` (#8001) * Make set_value a static method for Serializers As an alternative to #7671, let the method be overridden if needed. As the function is only used for serializers, it has a better place in the Serializer class. * Set `set_value` as an object (non-static) method * Add tests for set_value() These tests follow the examples given in the method. --- tests/test_serializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 9979c8e20b..3d52f3c0a7 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -843,4 +843,4 @@ def test_exception_raised_when_data_and_instance_length_different(self): instance=self.objs[:-1], many=True, partial=True, - ) \ No newline at end of file + ) From a1f03d5ee673113d5c03ed03bcd67523bc376fc0 Mon Sep 17 00:00:00 2001 From: Saadullah Aleem Date: Thu, 11 May 2023 01:19:12 +0500 Subject: [PATCH 11/19] fix: Make the instance variable of child serializer point to the correct list object instead of the entire list when validating ListSerializer --- rest_framework/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 12723c9dcd..4c1866bfc2 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -612,7 +612,7 @@ def __init__(self, *args, **kwargs): if kwargs.get('instance', []) and kwargs.get('data', []): assert len(kwargs.get("data", [])) == len( - kwargs.get("instance", [])), 'Data and instance should have same length' + kwargs.get("instance", [])), 'Data and instance should have same length' super().__init__(*args, **kwargs) self.child.bind(field_name='', parent=self) From 9ac6417aade0b3f54c5ee1be0a561f413bc26482 Mon Sep 17 00:00:00 2001 From: Saadullah Aleem Date: Thu, 11 May 2023 01:19:12 +0500 Subject: [PATCH 12/19] fix: Make the instance variable of child serializer point to the correct list object instead of the entire list when validating ListSerializer --- tests/test_serializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 3d52f3c0a7..9979c8e20b 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -843,4 +843,4 @@ def test_exception_raised_when_data_and_instance_length_different(self): instance=self.objs[:-1], many=True, partial=True, - ) + ) \ No newline at end of file From 76110bf8a5c3a0e91be3bd7f9cefa6ea36060467 Mon Sep 17 00:00:00 2001 From: Saadullah Aleem Date: Thu, 11 May 2023 01:33:19 +0500 Subject: [PATCH 13/19] fix formatting issues for list serializer validation fix --- rest_framework/serializers.py | 2 +- tests/test_serializer.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 4c1866bfc2..12723c9dcd 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -612,7 +612,7 @@ def __init__(self, *args, **kwargs): if kwargs.get('instance', []) and kwargs.get('data', []): assert len(kwargs.get("data", [])) == len( - kwargs.get("instance", [])), 'Data and instance should have same length' + kwargs.get("instance", [])), 'Data and instance should have same length' super().__init__(*args, **kwargs) self.child.bind(field_name='', parent=self) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 9979c8e20b..b82a796369 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -11,7 +11,6 @@ from rest_framework import exceptions, fields, relations, serializers from rest_framework.fields import Field - from .models import ( ForeignKeyTarget, NestedForeignKeySource, NullableForeignKeySource ) @@ -837,10 +836,10 @@ def test_multiple_objects_are_validated_separately(self): def test_exception_raised_when_data_and_instance_length_different(self): with self.assertRaises(AssertionError): - serializer = MyClassSerializer( + MyClassSerializer( data=[{'value': 'set', 'id': instance.id} for instance in self.objs], instance=self.objs[:-1], many=True, partial=True, - ) \ No newline at end of file + ) From c1ee7e75b1bf894311df0cce40ee0dd5e9ef141e Mon Sep 17 00:00:00 2001 From: Saadullah Aleem Date: Thu, 11 May 2023 01:19:12 +0500 Subject: [PATCH 14/19] fix: Make the instance variable of child serializer point to the correct list object instead of the entire list when validating ListSerializer --- tests/test_serializer.py | 62 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index b82a796369..52a154dc51 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -3,6 +3,7 @@ import re import sys import unittest + from collections import ChainMap from collections.abc import Mapping @@ -11,6 +12,7 @@ from rest_framework import exceptions, fields, relations, serializers from rest_framework.fields import Field + from .models import ( ForeignKeyTarget, NestedForeignKeySource, NullableForeignKeySource ) @@ -843,3 +845,63 @@ def test_exception_raised_when_data_and_instance_length_different(self): many=True, partial=True, ) + + +class MyClass(models.Model): + name = models.CharField(max_length=100) + value = models.CharField(max_length=100, blank=True) + + app_label = "test" + + @property + def is_valid(self): + return self.name == 'valid' + + +class MyClassSerializer(serializers.ModelSerializer): + class Meta: + model = MyClass + fields = ('id', 'name', 'value') + + def validate_value(self, value): + if value and not self.instance.is_valid: + raise serializers.ValidationError( + 'Status cannot be set for invalid instance') + return value + + +class TestMultipleObjectsValidation(unittest.TestCase): + def setUp(self): + self.objs = [ + MyClass(name='valid'), + MyClass(name='invalid'), + MyClass(name='other'), + ] + + def test_multiple_objects_are_validated_separately(self): + + serializer = MyClassSerializer( + data=[{'value': 'set', 'id': instance.id} for instance in + self.objs], + instance=self.objs, + many=True, + partial=True, + ) + + assert not serializer.is_valid() + assert serializer.errors == [ + {}, + {'value': ['Status cannot be set for invalid instance']}, + {'value': ['Status cannot be set for invalid instance']} + ] + + def test_exception_raised_when_data_and_instance_length_different(self): + + with self.assertRaises(AssertionError): + serializer = MyClassSerializer( + data=[{'value': 'set', 'id': instance.id} for instance in + self.objs], + instance=self.objs[:-1], + many=True, + partial=True, + ) \ No newline at end of file From 74cb5002f58e5a424d3b4a9f61b446e59988f184 Mon Sep 17 00:00:00 2001 From: Saadullah Aleem Date: Thu, 11 May 2023 01:33:19 +0500 Subject: [PATCH 15/19] fix formatting issues for list serializer validation fix --- tests/test_serializer.py | 61 ---------------------------------------- 1 file changed, 61 deletions(-) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 52a154dc51..f89dd08719 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -3,7 +3,6 @@ import re import sys import unittest - from collections import ChainMap from collections.abc import Mapping @@ -844,64 +843,4 @@ def test_exception_raised_when_data_and_instance_length_different(self): instance=self.objs[:-1], many=True, partial=True, - ) - - -class MyClass(models.Model): - name = models.CharField(max_length=100) - value = models.CharField(max_length=100, blank=True) - - app_label = "test" - - @property - def is_valid(self): - return self.name == 'valid' - - -class MyClassSerializer(serializers.ModelSerializer): - class Meta: - model = MyClass - fields = ('id', 'name', 'value') - - def validate_value(self, value): - if value and not self.instance.is_valid: - raise serializers.ValidationError( - 'Status cannot be set for invalid instance') - return value - - -class TestMultipleObjectsValidation(unittest.TestCase): - def setUp(self): - self.objs = [ - MyClass(name='valid'), - MyClass(name='invalid'), - MyClass(name='other'), - ] - - def test_multiple_objects_are_validated_separately(self): - - serializer = MyClassSerializer( - data=[{'value': 'set', 'id': instance.id} for instance in - self.objs], - instance=self.objs, - many=True, - partial=True, - ) - - assert not serializer.is_valid() - assert serializer.errors == [ - {}, - {'value': ['Status cannot be set for invalid instance']}, - {'value': ['Status cannot be set for invalid instance']} - ] - - def test_exception_raised_when_data_and_instance_length_different(self): - - with self.assertRaises(AssertionError): - serializer = MyClassSerializer( - data=[{'value': 'set', 'id': instance.id} for instance in - self.objs], - instance=self.objs[:-1], - many=True, - partial=True, ) \ No newline at end of file From 7197a86428e2c95895dcf9e0f522885d806e34a2 Mon Sep 17 00:00:00 2001 From: Saadullah Aleem Date: Sat, 27 May 2023 16:39:26 +0500 Subject: [PATCH 16/19] fix linting --- tests/test_serializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index f89dd08719..39d9238ef9 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -843,4 +843,4 @@ def test_exception_raised_when_data_and_instance_length_different(self): instance=self.objs[:-1], many=True, partial=True, - ) \ No newline at end of file + ) From bb4102a162bcfe49492ac47c42daf42933c00343 Mon Sep 17 00:00:00 2001 From: Asif Saif Uddin Date: Sat, 27 May 2023 18:17:51 +0600 Subject: [PATCH 17/19] Update rest_framework/serializers.py Co-authored-by: Sergei Shishov --- rest_framework/serializers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 12723c9dcd..237dbbc705 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -689,8 +689,11 @@ def to_internal_value(self, data): errors = [] for idx, item in enumerate(data): - if hasattr(self, 'instance') and self.instance and \ - len(self.instance) > idx: + if ( + hasattr(self, 'instance') + and self.instance + and len(self.instance) > idx + ): self.child.instance = self.instance[idx] try: validated = self.child.run_validation(item) From 0dc62010b17c79d5b4c0aafdab525af28d71d379 Mon Sep 17 00:00:00 2001 From: Saad Aleem Date: Sat, 27 May 2023 21:27:25 +0500 Subject: [PATCH 18/19] Update rest_framework/serializers.py Co-authored-by: Sergei Shishov --- rest_framework/serializers.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 237dbbc705..9db5136479 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -610,9 +610,11 @@ def __init__(self, *args, **kwargs): assert self.child is not None, '`child` is a required argument.' assert not inspect.isclass(self.child), '`child` has not been instantiated.' - if kwargs.get('instance', []) and kwargs.get('data', []): - assert len(kwargs.get("data", [])) == len( - kwargs.get("instance", [])), 'Data and instance should have same length' + # instance, data = kwargs.get('instance', []), kwargs.get('data', []) # if you prefer one line assignment support + instance = kwargs.get('instance', []) + data = kwargs.get('data', []) + if instance and data: + assert len(data) == len(instance), 'Data and instance should have same length' super().__init__(*args, **kwargs) self.child.bind(field_name='', parent=self) From d74744969546bcd4a57223abb3d5f7dd8badec50 Mon Sep 17 00:00:00 2001 From: Saadullah Aleem Date: Sun, 28 May 2023 22:57:51 +0500 Subject: [PATCH 19/19] fix: instance variable in list serializer, remove commented code --- rest_framework/serializers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 9db5136479..56fa918dc8 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -610,7 +610,6 @@ def __init__(self, *args, **kwargs): assert self.child is not None, '`child` is a required argument.' assert not inspect.isclass(self.child), '`child` has not been instantiated.' - # instance, data = kwargs.get('instance', []), kwargs.get('data', []) # if you prefer one line assignment support instance = kwargs.get('instance', []) data = kwargs.get('data', []) if instance and data: