Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit ff0f93a

Browse files
Avoid AttributeError for PUT and PATCH methods when using APIView (#778)
Fixes silenced AttributeError in the case when lookup_url_kwarg doesn't exist in a view
1 parent 5f19ef0 commit ff0f93a

File tree

3 files changed

+92
-3
lines changed

3 files changed

+92
-3
lines changed

‎CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
Note that in line with [Django REST Framework policy](http://www.django-rest-framework.org/topics/release-notes/),
99
any parts of the framework not mentioned in the documentation should generally be considered private API, and may be subject to change.
1010

11+
## [Unreleased]
12+
13+
### Fixed
14+
15+
* Avoid `AttributeError` for PUT and PATCH methods when using `APIView`
16+
1117
## [3.1.0] - 2020年02月08日
1218

1319
### Added

‎example/tests/test_parsers.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import json
22
from io import BytesIO
33

4+
from django.conf.urls import url
45
from django.test import TestCase, override_settings
6+
from django.urls import reverse
7+
from rest_framework import views, status
58
from rest_framework.exceptions import ParseError
9+
from rest_framework.response import Response
10+
from rest_framework.test import APITestCase
611

12+
from rest_framework_json_api import serializers
713
from rest_framework_json_api.parsers import JSONParser
14+
from rest_framework_json_api.renderers import JSONRenderer
815

916

1017
class TestJSONParser(TestCase):
@@ -69,3 +76,78 @@ def test_parse_invalid_data_key(self):
6976

7077
with self.assertRaises(ParseError):
7178
parser.parse(stream, None, self.parser_context)
79+
80+
81+
class DummyDTO:
82+
def __init__(self, response_dict):
83+
for k, v in response_dict.items():
84+
setattr(self, k, v)
85+
86+
@property
87+
def pk(self):
88+
return self.id if hasattr(self, 'id') else None
89+
90+
91+
class DummySerializer(serializers.Serializer):
92+
body = serializers.CharField()
93+
id = serializers.IntegerField()
94+
95+
96+
class DummyAPIView(views.APIView):
97+
parser_classes = [JSONParser]
98+
renderer_classes = [JSONRenderer]
99+
resource_name = 'dummy'
100+
101+
def patch(self, request, *args, **kwargs):
102+
serializer = DummySerializer(DummyDTO(request.data))
103+
return Response(status=status.HTTP_200_OK, data=serializer.data)
104+
105+
106+
urlpatterns = [
107+
url(r'repeater$', DummyAPIView.as_view(), name='repeater'),
108+
]
109+
110+
111+
class TestParserOnAPIView(APITestCase):
112+
113+
def setUp(self):
114+
class MockRequest(object):
115+
def __init__(self):
116+
self.method = 'PATCH'
117+
118+
request = MockRequest()
119+
# To be honest view string isn't resolved into actual view
120+
self.parser_context = {'request': request, 'kwargs': {}, 'view': 'DummyAPIView'}
121+
122+
self.data = {
123+
'data': {
124+
'id': 123,
125+
'type': 'strs',
126+
'attributes': {
127+
'body': 'hello'
128+
},
129+
}
130+
}
131+
132+
self.string = json.dumps(self.data)
133+
134+
def test_patch_doesnt_raise_attribute_error(self):
135+
parser = JSONParser()
136+
137+
stream = BytesIO(self.string.encode('utf-8'))
138+
139+
data = parser.parse(stream, None, self.parser_context)
140+
141+
assert data['id'] == 123
142+
assert data['body'] == 'hello'
143+
144+
@override_settings(ROOT_URLCONF=__name__)
145+
def test_patch_request(self):
146+
url = reverse('repeater')
147+
data = self.data
148+
data['data']['type'] = 'dummy'
149+
response = self.client.patch(url, data=data)
150+
data = response.json()
151+
152+
assert data['data']['id'] == str(123)
153+
assert data['data']['attributes']['body'] == 'hello'

‎rest_framework_json_api/parsers.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,13 +144,14 @@ def parse(self, stream, media_type=None, parser_context=None):
144144
raise ParseError("The resource identifier object must contain an 'id' member")
145145

146146
if request.method in ('PATCH', 'PUT'):
147-
lookup_url_kwarg = view.lookup_url_kwarg or view.lookup_field
148-
if str(data.get('id')) != str(view.kwargs[lookup_url_kwarg]):
147+
lookup_url_kwarg = getattr(view, 'lookup_url_kwarg', None) or \
148+
getattr(view, 'lookup_field', None)
149+
if lookup_url_kwarg and str(data.get('id')) != str(view.kwargs[lookup_url_kwarg]):
149150
raise exceptions.Conflict(
150151
"The resource object's id ({data_id}) does not match url's "
151152
"lookup id ({url_id})".format(
152153
data_id=data.get('id'),
153-
url_id=view.kwargs[view.lookup_field]
154+
url_id=view.kwargs[lookup_url_kwarg]
154155
)
155156
)
156157

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /