diff --git a/AUTHORS b/AUTHORS index 0e2e1902..1619377a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,6 +2,7 @@ Adam Wróbel Adam Ziolkowski Alan Crosswell Anton Shutik +Boris Pleshakov Christian Zosel David Vogt Greg Aker diff --git a/CHANGELOG.md b/CHANGELOG.md index eb93ab14..57cef3d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ any parts of the framework not mentioned in the documentation should generally b * Added support for Django REST framework 3.11 * Added support for Django 3.0 +### Fixed + +* Ensure that `409 Conflict` is returned when processing a `PATCH` request in which the resource object’s type and id do not match the server’s endpoint properly as outlined in [JSON:API](https://jsonapi.org/format/#crud-updating-responses-409) spec. + ## [3.0.0] - 2019-10-14 This release is not backwards compatible. For easy migration best upgrade first to version diff --git a/example/tests/test_model_viewsets.py b/example/tests/test_model_viewsets.py index 820c8931..1ce8336d 100644 --- a/example/tests/test_model_viewsets.py +++ b/example/tests/test_model_viewsets.py @@ -185,6 +185,24 @@ def test_patch_requires_id(self): self.assertEqual(response.status_code, 400) + def test_patch_requires_correct_id(self): + """ + Verify that 'id' is the same then in url + """ + data = { + 'data': { + 'type': 'users', + 'id': self.miles.pk + 1, + 'attributes': { + 'first-name': 'DifferentName' + } + } + } + + response = self.client.patch(self.detail_url, data=data) + + self.assertEqual(response.status_code, 409) + def test_key_in_post(self): """ Ensure a key is in the post. diff --git a/rest_framework_json_api/parsers.py b/rest_framework_json_api/parsers.py index 2742302c..5b024499 100644 --- a/rest_framework_json_api/parsers.py +++ b/rest_framework_json_api/parsers.py @@ -139,6 +139,17 @@ def parse(self, stream, media_type=None, parser_context=None): if not data.get('id') and request.method in ('PATCH', 'PUT'): raise ParseError("The resource identifier object must contain an 'id' member") + if request.method in ('PATCH', 'PUT'): + lookup_url_kwarg = view.lookup_url_kwarg or view.lookup_field + if str(data.get('id')) != str(view.kwargs[lookup_url_kwarg]): + raise exceptions.Conflict( + "The resource object's id ({data_id}) does not match url's " + "lookup id ({url_id})".format( + data_id=data.get('id'), + url_id=view.kwargs[view.lookup_field] + ) + ) + # Construct the return data serializer_class = getattr(view, 'serializer_class', None) parsed_data = {'id': data.get('id')} if 'id' in data else {}