diff --git a/docs/api.md b/docs/api.md index a7f8926f..86116d5c 100644 --- a/docs/api.md +++ b/docs/api.md @@ -47,3 +47,14 @@ Calls a `get_root_meta` function on a serializer, if it exists. `build_json_resource_obj(fields, resource, resource_instance, resource_name)` Builds the resource object (type, id, attributes) and extracts relationships. + +## rest_framework_json_api.parsers.JSONParser + +Similar to `JSONRenderer`, the `JSONParser` you may override the following methods if you need +highly custom parsing control. + +#### parse_metadata + +`parse_metadata(result)` + +Returns a dictionary which will be merged into parsed data of the request. By default, it reads the `meta` content in the request body and returns it in a dictionary with a `_meta` top level key. diff --git a/docs/usage.md b/docs/usage.md index 68196174..c1c966b7 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -375,7 +375,7 @@ class LineItemViewSet(viewsets.ModelViewSet): ### RelationshipView `rest_framework_json_api.views.RelationshipView` is used to build -relationship views (see the +relationship views (see the [JSON API spec](http://jsonapi.org/format/#fetching-relationships)). The `self` link on a relationship object should point to the corresponding relationship view. @@ -449,6 +449,8 @@ def get_root_meta(self, resource, many): ``` to the serializer. It must return a dict and will be merged with the existing top level `meta`. +To access metadata in incoming requests, the `JSONParser` will add the metadata under a top level `_meta` key in the parsed data dictionary. For instance, to access meta data from a `serializer` object, you may use `serializer.initial_data.get("_meta")`. To customize the `_meta` key, see [here](api.md). + ### Links Adding `url` to `fields` on a serializer will add a `self` link to the `links` key. diff --git a/example/tests/test_parsers.py b/example/tests/test_parsers.py new file mode 100644 index 00000000..9da1f09c --- /dev/null +++ b/example/tests/test_parsers.py @@ -0,0 +1,38 @@ +import json + +from django.test import TestCase +from io import BytesIO +from rest_framework_json_api.parsers import JSONParser + + +class TestJSONParser(TestCase): + + def setUp(self): + class MockRequest(object): + + def __init__(self): + self.method = 'GET' + + request = MockRequest() + + self.parser_context = {'request': request, 'kwargs': {}, 'view': 'BlogViewSet'} + + data = { + 'data': { + 'id': 123, + 'type': 'Blog' + }, + 'meta': { + 'random_key': 'random_value' + } + } + + self.string = json.dumps(data) + + def test_parse_include_metadata(self): + parser = JSONParser() + + stream = BytesIO(self.string.encode('utf-8')) + data = parser.parse(stream, None, self.parser_context) + + self.assertEqual(data['_meta'], {'random_key': 'random_value'}) diff --git a/rest_framework_json_api/parsers.py b/rest_framework_json_api/parsers.py index dc24a2df..a0c53f05 100644 --- a/rest_framework_json_api/parsers.py +++ b/rest_framework_json_api/parsers.py @@ -46,6 +46,14 @@ def parse_relationships(data): parsed_relationships[field_name] = list(relation for relation in field_data) return parsed_relationships + @staticmethod + def parse_metadata(result): + metadata = result.get('meta') + if metadata: + return {'_meta': metadata} + else: + return {} + def parse(self, stream, media_type=None, parser_context=None): """ Parses the incoming bytestream as JSON and returns the resulting data @@ -87,6 +95,7 @@ def parse(self, stream, media_type=None, parser_context=None): parsed_data = {'id': data.get('id')} parsed_data.update(self.parse_attributes(data)) parsed_data.update(self.parse_relationships(data)) + parsed_data.update(self.parse_metadata(result)) return parsed_data else: