From 5e7497bfe04fb91c1cdb70f7649b22325c9a78de Mon Sep 17 00:00:00 2001 From: sol HYUN Date: Mon, 5 Dec 2016 18:02:09 +0900 Subject: [PATCH 1/9] modify _writable_fields in serializers.py Sometimes 'self' instance is not a field (used in rest_framework) If the 'serializer' that contains 'field' is 'self', only the subelements of the serializer, the 'fields', are returned. in the case of a serializer that contains 'field' as a 'child' element, Only instances of `.source` ending with` _set '(following relationships "backward") are found using regular expressions. Then use the child elements again to find the field using '_writable_fields'. Do not modify '_readable_fields' because it does not address the issue of what is currently being viewed. --- rest_framework/serializers.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 02c24b70e..8e7206fd8 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -362,10 +362,15 @@ class Serializer(BaseSerializer): @cached_property def _writable_fields(self): - return [ - field for field in self.fields.values() - if (not field.read_only) or (field.default is not empty) - ] + ret = [] + for field in self.fields.values(): + if re.compile("\_set$").findall(field.source): + for field in field.child._writable_fields: + ret.append(field) + continue + if (not field.read_only) or (field.default is not empty): + ret.append(field) + return ret @cached_property def _readable_fields(self): From d4cc5478ddf5b70034e2e8ec58031e58d52c4308 Mon Sep 17 00:00:00 2001 From: sol HYUN Date: Mon, 5 Dec 2016 18:51:03 +0900 Subject: [PATCH 2/9] modify get_value method for get multi-value Because dictionary is queryDict, If you send multiple values with the same field_name, When using the '__get__' method, only the last element is reflected. Use the 'getlist' method to return a list with multiple values --- rest_framework/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 13b5145ba..de279f292 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -422,7 +422,7 @@ class Field(object): if getattr(self.root, 'partial', False): return empty return self.default_empty_html - ret = dictionary[self.field_name] + ret = dictionary.getlist(self.field_name) if ret == '' and self.allow_null: # If the field is blank, and null is a valid value then # determine if we should use null instead. From c66df67c4946c52ccd70230299b49459dadd7e29 Mon Sep 17 00:00:00 2001 From: sol HYUN Date: Mon, 5 Dec 2016 19:16:01 +0900 Subject: [PATCH 3/9] Modify 'to_internal_value' method for handling list modified the 'get_value' method in fields.py, The value of primitive_value is a list of values to match the field. Validate the primitive_value (list) elements one by one using the run_validation method. The verified value is appended to the validated_list Finally, only when there are two or more elements of validated_list, they are returned as a list. --- rest_framework/serializers.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 8e7206fd8..cec87e881 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -454,26 +454,29 @@ class Serializer(BaseSerializer): ret = OrderedDict() errors = OrderedDict() fields = self._writable_fields - for field in fields: validate_method = getattr(self, 'validate_' + field.field_name, None) primitive_value = field.get_value(data) - try: - validated_value = field.run_validation(primitive_value) - if validate_method is not None: - validated_value = validate_method(validated_value) - except ValidationError as exc: - errors[field.field_name] = exc.detail - except DjangoValidationError as exc: - errors[field.field_name] = get_error_detail(exc) - except SkipField: - pass + validated_list = [] + for inner_data in primitive_value: + try: + validated_value = field.run_validation(inner_data) + validated_list.append(validated_value) + if validate_method is not None: + validated_list.append(validate_method(validated_value)) + except ValidationError as exc: + errors[field.field_name] = exc.detail + except DjangoValidationError as exc: + errors[field.field_name] = get_error_detail(exc) + except SkipField: + pass else: - set_value(ret, field.source_attrs, validated_value) - + if len(validated_list) > 1: + set_value(ret, field.source_attrs, validated_list) + else: + set_value(ret, field.source_attrs, validated_list[0]) if errors: raise ValidationError(errors) - return ret def to_representation(self, instance): From 8e332ad16f8c88cd698adc4fce2e96aa306623d6 Mon Sep 17 00:00:00 2001 From: sol HYUN Date: Mon, 5 Dec 2016 21:43:35 +0900 Subject: [PATCH 4/9] add import re --- 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 cec87e881..01b62a089 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -11,7 +11,7 @@ python primitives. response content is handled by parsers and renderers. """ from __future__ import unicode_literals - +import re import copy import inspect import traceback From 7ed8a480a3b0c4b302a4715feb8f57a1202e202d Mon Sep 17 00:00:00 2001 From: sol HYUN Date: Tue, 6 Dec 2016 00:18:33 +0900 Subject: [PATCH 5/9] Create lists only under certain conditions --- rest_framework/fields.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index de279f292..96dfc5831 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -422,7 +422,10 @@ class Field(object): if getattr(self.root, 'partial', False): return empty return self.default_empty_html - ret = dictionary.getlist(self.field_name) + if len(dictionary.getlist(self.field_name)) > 1: + ret = dictionary.getlist(self.field_name) + else: + ret = dictionary.get(self.field_name) if ret == '' and self.allow_null: # If the field is blank, and null is a valid value then # determine if we should use null instead. From f7b2829b03a1bd22c38d750c2df9f1450fe66d8c Mon Sep 17 00:00:00 2001 From: sol HYUN Date: Tue, 6 Dec 2016 00:20:08 +0900 Subject: [PATCH 6/9] Create lists only under certain conditions --- rest_framework/serializers.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 01b62a089..7be25fcbd 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -11,9 +11,10 @@ python primitives. response content is handled by parsers and renderers. """ from __future__ import unicode_literals -import re + import copy import inspect +import re import traceback from collections import OrderedDict @@ -458,6 +459,8 @@ class Serializer(BaseSerializer): validate_method = getattr(self, 'validate_' + field.field_name, None) primitive_value = field.get_value(data) validated_list = [] + if not isinstance(primitive_value, list): + primitive_value = [primitive_value, ] for inner_data in primitive_value: try: validated_value = field.run_validation(inner_data) @@ -471,10 +474,12 @@ class Serializer(BaseSerializer): except SkipField: pass else: - if len(validated_list) > 1: + if isinstance(validated_list[-1], str): + set_value(ret, field.source_attrs, validated_list[-1]) + elif len(validated_list) > 1: set_value(ret, field.source_attrs, validated_list) else: - set_value(ret, field.source_attrs, validated_list[0]) + set_value(ret, field.source_attrs, validated_list[-1]) if errors: raise ValidationError(errors) return ret From 8d441e31d1214cb365d7c96f621ee978dd648051 Mon Sep 17 00:00:00 2001 From: sol HYUN Date: Tue, 6 Dec 2016 00:56:44 +0900 Subject: [PATCH 7/9] 'get_value method' is reverted If I manipulate the List, I get a lot of errors. Although multi-upload operations are limited, It works fine when doing a single upload from a nested serializer. --- rest_framework/fields.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 96dfc5831..13b5145ba 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -422,10 +422,7 @@ class Field(object): if getattr(self.root, 'partial', False): return empty return self.default_empty_html - if len(dictionary.getlist(self.field_name)) > 1: - ret = dictionary.getlist(self.field_name) - else: - ret = dictionary.get(self.field_name) + ret = dictionary[self.field_name] if ret == '' and self.allow_null: # If the field is blank, and null is a valid value then # determine if we should use null instead. From 50c51c335e70836408b4d70be2399835eeb5b9ca Mon Sep 17 00:00:00 2001 From: sol HYUN Date: Tue, 6 Dec 2016 00:58:51 +0900 Subject: [PATCH 8/9] 'to_internal_value method' is reverted If I manipulate the list, I get a lot of errors. Although multi-upload operations are limited, It works fine when doing a single upload from a nested serializer. --- rest_framework/serializers.py | 37 ++++++++++++++--------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 7be25fcbd..9e47f4868 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -439,7 +439,7 @@ class Serializer(BaseSerializer): raise ValidationError(detail=as_serializer_error(exc)) return value - + def to_internal_value(self, data): """ Dict of native values <- Dict of primitive datatypes. @@ -455,33 +455,26 @@ class Serializer(BaseSerializer): ret = OrderedDict() errors = OrderedDict() fields = self._writable_fields + for field in fields: validate_method = getattr(self, 'validate_' + field.field_name, None) primitive_value = field.get_value(data) - validated_list = [] - if not isinstance(primitive_value, list): - primitive_value = [primitive_value, ] - for inner_data in primitive_value: - try: - validated_value = field.run_validation(inner_data) - validated_list.append(validated_value) - if validate_method is not None: - validated_list.append(validate_method(validated_value)) - except ValidationError as exc: - errors[field.field_name] = exc.detail - except DjangoValidationError as exc: - errors[field.field_name] = get_error_detail(exc) - except SkipField: - pass + try: + validated_value = field.run_validation(primitive_value) + if validate_method is not None: + validated_value = validate_method(validated_value) + except ValidationError as exc: + errors[field.field_name] = exc.detail + except DjangoValidationError as exc: + errors[field.field_name] = get_error_detail(exc) + except SkipField: + pass else: - if isinstance(validated_list[-1], str): - set_value(ret, field.source_attrs, validated_list[-1]) - elif len(validated_list) > 1: - set_value(ret, field.source_attrs, validated_list) - else: - set_value(ret, field.source_attrs, validated_list[-1]) + set_value(ret, field.source_attrs, validated_value) + if errors: raise ValidationError(errors) + return ret def to_representation(self, instance): From f5d6925312761a03d52e3e9be15c8644f38db3fe Mon Sep 17 00:00:00 2001 From: sol HYUN Date: Tue, 6 Dec 2016 01:00:51 +0900 Subject: [PATCH 9/9] Modify '_writable_fields Method' Reinforce against errors. --- rest_framework/serializers.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 9e47f4868..e93be3571 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -366,8 +366,13 @@ class Serializer(BaseSerializer): ret = [] for field in self.fields.values(): if re.compile("\_set$").findall(field.source): - for field in field.child._writable_fields: - ret.append(field) + try: + for field in field.child._writable_fields: + if (not field.read_only) or (field.default is not empty): + ret.append(field) + except: + if (not field.read_only) or (field.default is not empty): + ret.append(field) continue if (not field.read_only) or (field.default is not empty): ret.append(field) @@ -439,7 +444,7 @@ class Serializer(BaseSerializer): raise ValidationError(detail=as_serializer_error(exc)) return value - + def to_internal_value(self, data): """ Dict of native values <- Dict of primitive datatypes.