From 5e7497bfe04fb91c1cdb70f7649b22325c9a78de Mon Sep 17 00:00:00 2001 From: sol HYUN Date: Mon, 5 Dec 2016 18:02:09 +0900 Subject: [PATCH 1/3] 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/3] 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/3] 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):