From af963701ee020f71ab2fe51331a35aac1c7e2d85 Mon Sep 17 00:00:00 2001 From: Kiptoo Magutt Date: Fri, 2 Feb 2018 15:02:37 +0300 Subject: [PATCH] issue#679 - try to deserialize patch request data before updating instance - the idea is to deserialize the input fields into their actual representations so that the setattr method of _update_instance() sets the real instance values and that custom serialization of enum fields works as expected - this approach causes most unit tests in test_updating.py to fail :( due to the GeneratedIDNotAllowed exception. workaround? - https://github.com/jfinkels/flask-restless/issues/679 --- flask_restless/views/resources.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/flask_restless/views/resources.py b/flask_restless/views/resources.py index f4fe1f4b..8c10e2a9 100644 --- a/flask_restless/views/resources.py +++ b/flask_restless/views/resources.py @@ -654,13 +654,13 @@ def patch(self, resource_id): """ # try to load the fields/values to update from the body of the request try: - data = json.loads(request.get_data()) or {} + document = json.loads(request.get_data()) or {} except (BadRequest, TypeError, ValueError, OverflowError) as exception: # this also happens when request.data is empty detail = 'Unable to decode data' return error_response(400, cause=exception, detail=detail) for preprocessor in self.preprocessors['PATCH_RESOURCE']: - temp_result = preprocessor(resource_id=resource_id, data=data) + temp_result = preprocessor(resource_id=resource_id, data=document) # See the note under the preprocessor in the get() method. if temp_result is not None: resource_id = temp_result @@ -681,8 +681,27 @@ def patch(self, resource_id): detail = 'No resource found with type {0} and ID {1}' detail = detail.format(collection_name(self.model), resource_id) return error_response(404, detail=detail) - # Unwrap the data from the collection name key. - data = data.pop('data', {}) + # deserialize the partial document to extract values that + # have non-string/unicode representations of model fields, e.g enums + # this is necessary because :meth:`_update_instance` sets instance + # attributes to literal values parsed from data.items(), yet that + # method does not commit the changes to the database before possibly + # calling the model's serializer. serializing fields that have + # non-literal values would otherwise fail + try: + raise Exception(self.deserializer.__dict__) + deserialized_instance = self.deserializer.deserialize(document) + # Unwrap the data from the collection name key. + data = document.pop('data', {}) + # replace attributes with ones from the deserialized instance + if data: + attributes_ = data.get('attributes', {}) + for field in attributes_.keys(): + value = vars(deserialized_instance)[field] + attributes_[field] = value + except self.validation_exceptions as exception: + return self._handle_validation_exception(exception) + if 'type' not in data: detail = 'Missing "type" element' return error_response(400, detail=detail)