Skip to content

Commit 8c45bff

Browse files
committed
Merge pull request #60 from django-json-api/feature/JsonAPISpec
Fixed the parser
2 parents 4075cbe + f4b38e4 commit 8c45bff

File tree

5 files changed

+71
-32
lines changed

5 files changed

+71
-32
lines changed

example/tests/test_model_viewsets.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import json
22

3-
from example.tests import TestBase
4-
53
from django.contrib.auth import get_user_model
64
from django.utils import encoding
75
from django.core.urlresolvers import reverse
8-
from django.conf import settings
6+
7+
from example.tests import TestBase
98

109

1110
class ModelViewSetTests(TestBase):
@@ -167,7 +166,7 @@ def test_key_in_post(self):
167166
}
168167
}
169168
data_attributes = data['data']['attributes']
170-
response = self.client.put(self.detail_url, data=data, format='json')
169+
response = self.client.put(self.detail_url, content_type='application/vnd.api+json', data=json.dumps(data))
171170

172171
result = json.loads(response.content.decode('utf8'))
173172
result_attributes = result['data']['attributes']

rest_framework_json_api/exceptions.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
2-
from django.utils import six, encoding
1+
from django.utils import encoding
2+
from django.utils.translation import ugettext_lazy as _
3+
from rest_framework import status
4+
from rest_framework.exceptions import APIException
35
from rest_framework.views import exception_handler as drf_exception_handler
6+
47
from rest_framework_json_api.utils import format_value
58

69

@@ -39,3 +42,8 @@ def exception_handler(exc, context):
3942
context['view'].resource_name = 'errors'
4043
response.data = errors
4144
return response
45+
46+
47+
class Conflict(APIException):
48+
status_code = status.HTTP_409_CONFLICT
49+
default_detail = _('Conflict.')

rest_framework_json_api/parsers.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44
from rest_framework import parsers
55

6-
from . import utils
6+
from . import utils, renderers, exceptions
77

88

99
class JSONParser(parsers.JSONParser):
@@ -23,14 +23,47 @@ class JSONParser(parsers.JSONParser):
2323
2424
We extract the attributes so that DRF serializers can work as normal.
2525
"""
26+
media_type = 'application/vnd.api+json'
27+
renderer_class = renderers.JSONRenderer
28+
2629
def parse(self, stream, media_type=None, parser_context=None):
2730
"""
2831
Parses the incoming bytestream as JSON and returns the resulting data
2932
"""
30-
result = super(JSONParser, self).parse(stream, media_type=media_type,
31-
parser_context=parser_context)
33+
result = super(JSONParser, self).parse(stream, media_type=media_type, parser_context=parser_context)
3234
data = result.get('data', {})
33-
attributes = data.get('attributes')
34-
if attributes:
35-
attributes['id'] = data.get('id')
36-
return utils.format_keys(attributes, 'underscore')
35+
36+
if data:
37+
# Check for inconsistencies
38+
resource_name = utils.get_resource_name(parser_context)
39+
if data.get('type') != resource_name:
40+
raise exceptions.Conflict(
41+
"The resource object's type ({data_type}) is not the type "
42+
"that constitute the collection represented by the endpoint ({resource_type}).".format(
43+
data_type=data.get('type'),
44+
resource_type=resource_name
45+
)
46+
)
47+
# Get the ID
48+
data_id = data.get('id')
49+
# Get the attributes
50+
attributes = utils.format_keys(data.get('attributes'), 'underscore') if data.get(
51+
'attributes') else dict()
52+
# Get the relationships
53+
relationships = utils.format_keys(data.get('relationships'), 'underscore') if data.get(
54+
'relationships') else dict()
55+
56+
# Parse the relationships
57+
parsed_relationships = dict()
58+
for field_name, field_data in relationships.items():
59+
field_data = field_data.get('data')
60+
if isinstance(field_data, dict):
61+
parsed_relationships[field_name] = field_data.get('id')
62+
elif isinstance(field_data, list):
63+
parsed_relationships[field_name] = list(relation.get('id') for relation in field_data)
64+
65+
# Construct the return data
66+
parsed_data = {'id': data_id}
67+
parsed_data.update(attributes)
68+
parsed_data.update(parsed_relationships)
69+
return parsed_data

rest_framework_json_api/renderers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
Renderers
33
"""
44
from collections import OrderedDict
5+
56
from rest_framework import renderers
67

78
from . import utils
8-
from rest_framework.relations import RelatedField
9-
from rest_framework.settings import api_settings
109

1110

1211
class JSONRenderer(renderers.JSONRenderer):
@@ -30,6 +29,7 @@ class JSONRenderer(renderers.JSONRenderer):
3029
"""
3130

3231
media_type = 'application/vnd.api+json'
32+
format = 'vnd.api+json'
3333

3434
def render(self, data, accepted_media_type=None, renderer_context=None):
3535
# Get the resource name.
@@ -47,7 +47,7 @@ def render(self, data, accepted_media_type=None, renderer_context=None):
4747

4848
# If this is an error response, skip the rest.
4949
if resource_name == 'errors':
50-
if len(data) > 1:
50+
if len(data) > 1 and isinstance(data, list):
5151
data.sort(key=lambda x: x.get('source', {}).get('pointer', ''))
5252
return super(JSONRenderer, self).render(
5353
{resource_name: data}, accepted_media_type, renderer_context

rest_framework_json_api/utils.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,17 @@
22
Utils.
33
"""
44
import inflection
5-
65
from django.core import urlresolvers
76
from django.conf import settings
87
from django.utils import six, encoding
9-
from django.utils.six.moves.urllib.parse import urlparse, urlunparse
108
from django.utils.translation import ugettext_lazy as _
11-
129
from rest_framework.serializers import BaseSerializer, ListSerializer, ModelSerializer
1310
from rest_framework.relations import RelatedField, HyperlinkedRelatedField, PrimaryKeyRelatedField
1411
from rest_framework.settings import api_settings
1512
from rest_framework.exceptions import APIException
1613

14+
from django.utils.six.moves.urllib.parse import urlparse
15+
1716
try:
1817
from rest_framework.compat import OrderedDict
1918
except ImportError:
@@ -178,9 +177,9 @@ def extract_id_from_url(url):
178177
def extract_id(fields, resource):
179178
for field_name, field in six.iteritems(fields):
180179
if field_name == 'id':
181-
return encoding.force_text(resource[field_name])
180+
return encoding.force_text(resource.get(field_name))
182181
if field_name == api_settings.URL_FIELD_NAME:
183-
return extract_id_from_url(resource[field_name])
182+
return extract_id_from_url(resource.get(field_name))
184183

185184

186185
def extract_attributes(fields, resource):
@@ -193,7 +192,7 @@ def extract_attributes(fields, resource):
193192
if isinstance(field, (RelatedField, BaseSerializer, ManyRelatedField)):
194193
continue
195194
data.update({
196-
field_name: resource[field_name]
195+
field_name: resource.get(field_name)
197196
})
198197

199198
return format_keys(data)
@@ -213,11 +212,11 @@ def extract_relationships(fields, resource):
213212
if isinstance(field, (PrimaryKeyRelatedField, HyperlinkedRelatedField)):
214213
relation_type = get_related_resource_type(field)
215214

216-
if resource[field_name] is not None:
215+
if resource.get(field_name) is not None:
217216
if isinstance(field, PrimaryKeyRelatedField):
218-
relation_id = encoding.force_text(resource[field_name])
217+
relation_id = encoding.force_text(resource.get(field_name))
219218
elif isinstance(field, HyperlinkedRelatedField):
220-
relation_id = extract_id_from_url(resource[field_name])
219+
relation_id = extract_id_from_url(resource.get(field_name))
221220
else:
222221
relation_id = None
223222

@@ -240,14 +239,14 @@ def extract_relationships(fields, resource):
240239
relation_type = get_related_resource_type(relation)
241240

242241
if isinstance(relation, HyperlinkedRelatedField):
243-
for link in resource[field_name]:
242+
for link in resource.get(field_name, list()):
244243
relation_data.append(OrderedDict([('type', relation_type), ('id', extract_id_from_url(link))]))
245244

246245
data.update({field_name: {'data': relation_data}})
247246
continue
248247

249248
if isinstance(relation, PrimaryKeyRelatedField):
250-
for pk in resource[field_name]:
249+
for pk in resource.get(field_name, list()):
251250
relation_data.append(OrderedDict([('type', relation_type), ('id', encoding.force_text(pk))]))
252251

253252
data.update({field_name: {'data': relation_data}})
@@ -262,7 +261,7 @@ def extract_relationships(fields, resource):
262261

263262
# Get the serializer fields
264263
serializer_fields = get_serializer_fields(serializer)
265-
serializer_data = resource[field_name]
264+
serializer_data = resource.get(field_name)
266265
if isinstance(serializer_data, list):
267266
for serializer_resource in serializer_data:
268267
relation_data.append(
@@ -279,14 +278,14 @@ def extract_relationships(fields, resource):
279278

280279
# Get the serializer fields
281280
serializer_fields = get_serializer_fields(field)
282-
serializer_data = resource[field_name]
281+
serializer_data = resource.get(field_name)
283282
data.update({
284283
field_name: {
285284
'data': (
286285
OrderedDict([
287286
('type', relation_type),
288287
('id', extract_id(serializer_fields, serializer_data))
289-
]) if resource[field_name] else None)
288+
]) if resource.get(field_name) else None)
290289
}
291290
})
292291
continue
@@ -313,7 +312,7 @@ def extract_included(fields, resource):
313312

314313
# Get the serializer fields
315314
serializer_fields = get_serializer_fields(serializer)
316-
serializer_data = resource[field_name]
315+
serializer_data = resource.get(field_name)
317316
if isinstance(serializer_data, list):
318317
for serializer_resource in serializer_data:
319318
included_data.append(build_json_resource_obj(serializer_fields, serializer_resource, relation_type))
@@ -325,7 +324,7 @@ def extract_included(fields, resource):
325324

326325
# Get the serializer fields
327326
serializer_fields = get_serializer_fields(field)
328-
serializer_data = resource[field_name]
327+
serializer_data = resource.get(field_name)
329328
if serializer_data:
330329
included_data.append(build_json_resource_obj(serializer_fields, serializer_data, relation_type))
331330

0 commit comments

Comments
 (0)