def test_get_resource_name(): view = APIView() context = {'view': view} setattr(settings, 'JSON_API_FORMAT_TYPES', None) assert 'APIViews' == utils.get_resource_name(context), 'not formatted' context = {'view': view} setattr(settings, 'JSON_API_FORMAT_TYPES', 'dasherize') assert 'api-views' == utils.get_resource_name(context), 'derived from view' view.model = get_user_model() assert 'users' == utils.get_resource_name(context), 'derived from view model' view.resource_name = 'custom' assert 'custom' == utils.get_resource_name(context), 'manually set on view' view.response = Response(status=403) assert 'errors' == utils.get_resource_name(context), 'handles 4xx error' view.response = Response(status=500) assert 'errors' == utils.get_resource_name(context), 'handles 500 error' view = GenericAPIView() view.serializer_class = ResourceSerializer context = {'view': view} assert 'users' == utils.get_resource_name(context), 'derived from serializer' view.serializer_class.Meta.resource_name = 'rcustom' assert 'rcustom' == utils.get_resource_name(context), 'set on serializer' view = GenericAPIView() view.serializer_class = NonModelResourceSerializer context = {'view': view} assert 'users' == utils.get_resource_name(context), 'derived from non-model serializer'
def test_get_resource_name(): view = APIView() context = {'view': view} with override_settings(JSON_API_FORMAT_TYPES=None): assert 'APIViews' == utils.get_resource_name(context), 'not formatted' context = {'view': view} with override_settings(JSON_API_FORMAT_TYPES='dasherize'): assert 'api-views' == utils.get_resource_name(context), 'derived from view' view.model = get_user_model() assert 'users' == utils.get_resource_name(context), 'derived from view model' view.resource_name = 'custom' assert 'custom' == utils.get_resource_name(context), 'manually set on view' view.response = Response(status=403) assert 'errors' == utils.get_resource_name(context), 'handles 4xx error' view.response = Response(status=500) assert 'errors' == utils.get_resource_name(context), 'handles 500 error' view = GenericAPIView() view.serializer_class = ResourceSerializer context = {'view': view} assert 'users' == utils.get_resource_name(context), 'derived from serializer' view.serializer_class.Meta.resource_name = 'rcustom' assert 'rcustom' == utils.get_resource_name(context), 'set on serializer' view = GenericAPIView() view.serializer_class = NonModelResourceSerializer context = {'view': view} assert 'users' == utils.get_resource_name(context), 'derived from non-model serializer'
def test_get_resource_name_from_view(settings, format_type, pluralize_type, output): settings.JSON_API_FORMAT_TYPES = format_type settings.JSON_API_PLURALIZE_TYPES = pluralize_type view = APIView() context = {"view": view} assert output == get_resource_name(context)
def field_to_swagger_object(self, field, swagger_object_type, use_references, included=False, is_request=None, **kwargs): if not self.is_json_api_root_serializer(field, is_request): if self.handle_json_api_only: return inspectors.NotHandled else: return super().field_to_swagger_object(field, swagger_object_type, use_references, **kwargs) if is_request is None and included: is_request = False if included: resource_name = get_resource_type_from_serializer(field) else: resource_name = get_resource_name(context={'view': self.view}) SwaggerType, ChildSwaggerType = self._get_partial_types( field, swagger_object_type, use_references, **kwargs) return self.build_serializer_schema(field, resource_name, SwaggerType, ChildSwaggerType, use_references, is_request)
def render(self, data, accepted_media_type=None, renderer_context=None): try: # Get the resource name. resource_name = utils.get_resource_name(renderer_context) # If this is an error response, skip the rest. if resource_name == 'errors': return self.render_errors(data, accepted_media_type, renderer_context) except: pass response = renderer_context['response'] if response.status_code == HTTP_204_NO_CONTENT: return super(CustomJsonRender, self).render(data=data, accepted_media_type=accepted_media_type, renderer_context=renderer_context) meta_data = {"include": [], "custom": []} if data and 'pagination' in data: meta_data["pagination"] = data.pop('pagination') data = data["data"] render_data = {"data": data, "meta": meta_data} return super(CustomJsonRender, self).render(data=render_data, accepted_media_type=accepted_media_type, renderer_context=renderer_context)
def test_get_resource_name_from_view_custom_resource_name( settings, format_type, pluralize_type): settings.JSON_API_FORMAT_TYPES = format_type settings.JSON_API_PLURALIZE_TYPES = pluralize_type view = APIView() view.resource_name = "custom" context = {"view": view} assert "custom" == get_resource_name(context)
def test_get_resource_name_from_model_serializer_class(settings, format_type, pluralize_type, output): settings.JSON_API_FORMAT_TYPES = format_type settings.JSON_API_PLURALIZE_TYPES = pluralize_type view = GenericAPIView() view.serializer_class = BasicModelSerializer context = {"view": view} assert output == get_resource_name(context)
def parse(self, stream, media_type=None, parser_context=None): """ Parses the incoming bytestream as JSON and returns the resulting data """ if hasattr(stream, 'raw_body'): result = stream.raw_body else: # Handles requests created by Django's test client, which is missing the raw_body attribute set in # the Django request-like object initialized by our zc_event event client try: result = ujson.loads(stream.body) except ValueError: result = {} data = result.get('data') if data: from rest_framework_json_api.views import RelationshipView if isinstance(parser_context['view'], RelationshipView): # We skip parsing the object as JSONAPI Resource Identifier Object and not a regular Resource Object if isinstance(data, list): for resource_identifier_object in data: if not (resource_identifier_object.get('id') and resource_identifier_object.get('type')): raise ParseError( 'Received data contains one or more malformed JSONAPI Resource Identifier Object(s)' ) elif not (data.get('id') and data.get('type')): raise ParseError( 'Received data is not a valid JSONAPI Resource Identifier Object' ) return data request = parser_context.get('request') # Check for inconsistencies resource_name = utils.get_resource_name(parser_context) view = parser_context.get('view') if data.get('type') != resource_name and request.method in ( 'PUT', 'POST', 'PATCH'): raise exceptions.Conflict( "The resource object's type ({data_type}) is not the type " "that constitute the collection represented by the endpoint ({resource_type})." .format(data_type=data.get('type'), resource_type=resource_name)) # Construct the return data 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: raise ParseError('Received document does not contain primary data')
def test_get_resource_name_from_model_serializer_class_custom_resource_name( settings, format_type, pluralize_type): settings.JSON_API_FORMAT_TYPES = format_type settings.JSON_API_PLURALIZE_TYPES = pluralize_type view = GenericAPIView() view.serializer_class = BasicModelSerializer view.serializer_class.Meta.resource_name = "custom" context = {"view": view} assert "custom" == get_resource_name(context)
def test_get_resource_name_from_plain_serializer_class(settings, format_type, pluralize_type): class PlainSerializer(serializers.Serializer): class Meta: resource_name = "custom" settings.JSON_API_FORMAT_TYPES = format_type settings.JSON_API_PLURALIZE_TYPES = pluralize_type view = GenericAPIView() view.serializer_class = PlainSerializer context = {"view": view} assert "custom" == get_resource_name(context)
def parse(self, stream, media_type=None, parser_context=None): """ Parses the incoming bytestream as JSON and returns the resulting data """ result = super(JSONParser, self).parse(stream, media_type=media_type, parser_context=parser_context) data = result.get('data') if data: from rest_framework_json_api.views import RelationshipView if isinstance(parser_context['view'], RelationshipView): # We skip parsing the object as JSONAPI Resource Identifier Object and not a regular Resource Object if isinstance(data, list): for resource_identifier_object in data: if not (resource_identifier_object.get('id') and resource_identifier_object.get('type')): raise ParseError( 'Received data contains one or more malformed JSONAPI Resource Identifier Object(s)' ) elif not (data.get('id') and data.get('type')): raise ParseError('Received data is not a valid JSONAPI Resource Identifier Object') return data request = parser_context.get('request') # Check for inconsistencies resource_name = utils.get_resource_name(parser_context) view = parser_context.get('view') if data.get('type') != resource_name and request.method in ('PUT', 'POST', 'PATCH'): raise exceptions.Conflict( "The resource object's type ({data_type}) is not the type " "that constitute the collection represented by the endpoint ({resource_type}).".format( data_type=data.get('type'), resource_type=resource_name ) ) # Construct the return data 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: raise ParseError('Received document does not contain primary data')
def field_to_swagger_object(self, field, swagger_object_type, use_references, included=False, is_request=None, **kwargs): if not self.is_json_api_root_serializer(field, is_request): return super().field_to_swagger_object(field, swagger_object_type, use_references, **kwargs) SwaggerType, ChildSwaggerType = self._get_partial_types( field, swagger_object_type, use_references, **kwargs) resource_name = get_resource_type_from_serializer(field) if included \ else get_resource_name(context={'view': self.view}) return self.build_json_resource_schema(field, resource_name, SwaggerType, ChildSwaggerType, use_references, is_request)
def render(self, data, accepted_media_type=None, renderer_context=None): renderer_context = renderer_context or {} view = renderer_context.get("view", None) request = renderer_context.get("request", None) # Get the resource name. resource_name = utils.get_resource_name(renderer_context) # If this is an error response, skip the rest. if resource_name == "errors": return self.render_errors(data, accepted_media_type, renderer_context) # if response.status_code is 204 then the data to be rendered must # be None response = renderer_context.get("response", None) if response is not None and response.status_code == 204: return super().render(None, accepted_media_type, renderer_context) from rest_framework_json_api.views import RelationshipView if isinstance(view, RelationshipView): return self.render_relationship_view(data, accepted_media_type, renderer_context) # If `resource_name` is set to None then render default as the dev # wants to build the output format manually. if resource_name is None or resource_name is False: return super().render(data, accepted_media_type, renderer_context) json_api_data = data # initialize json_api_meta with pagination meta or an empty dict json_api_meta = data.get("meta", {}) if isinstance(data, dict) else {} included_cache = defaultdict(dict) if data and "results" in data: serializer_data = data["results"] else: serializer_data = data serializer = getattr(serializer_data, "serializer", None) included_resources = utils.get_included_resources(request, serializer) if serializer is not None: # Extract root meta for any type of serializer json_api_meta.update( self.extract_root_meta(serializer, serializer_data)) if getattr(serializer, "many", False): json_api_data = list() for position in range(len(serializer_data)): resource = serializer_data[ position] # Get current resource resource_instance = serializer.instance[ position] # Get current instance if isinstance( serializer.child, rest_framework_json_api.serializers. PolymorphicModelSerializer, ): resource_serializer_class = ( serializer.child. get_polymorphic_serializer_for_instance( resource_instance)( context=serializer.child.context)) else: resource_serializer_class = serializer.child fields = utils.get_serializer_fields( resource_serializer_class) force_type_resolution = getattr( resource_serializer_class, "_poly_force_type_resolution", False) json_resource_obj = self.build_json_resource_obj( fields, resource, resource_instance, resource_name, serializer, force_type_resolution, ) json_api_data.append(json_resource_obj) self.extract_included( fields, resource, resource_instance, included_resources, included_cache, ) else: fields = utils.get_serializer_fields(serializer) force_type_resolution = getattr(serializer, "_poly_force_type_resolution", False) resource_instance = serializer.instance json_api_data = self.build_json_resource_obj( fields, serializer_data, resource_instance, resource_name, serializer, force_type_resolution, ) self.extract_included( fields, serializer_data, resource_instance, included_resources, included_cache, ) # Make sure we render data in a specific order render_data = OrderedDict() if isinstance(data, dict) and data.get("links"): render_data["links"] = data.get("links") # format the api root link list if view.__class__ and view.__class__.__name__ == "APIRoot": render_data["data"] = None render_data["links"] = json_api_data else: render_data["data"] = json_api_data if included_cache: if isinstance(json_api_data, list): objects = json_api_data else: objects = [json_api_data] for object in objects: obj_type = object.get("type") obj_id = object.get("id") if obj_type in included_cache and obj_id in included_cache[ obj_type]: del included_cache[obj_type][obj_id] if not included_cache[obj_type]: del included_cache[obj_type] if included_cache: render_data["included"] = list() for included_type in sorted(included_cache.keys()): for included_id in sorted( included_cache[included_type].keys()): render_data["included"].append( included_cache[included_type][included_id]) if json_api_meta: render_data["meta"] = utils.format_field_names(json_api_meta) return super().render(render_data, accepted_media_type, renderer_context)
def render(self, data, accepted_media_type=None, renderer_context=None): renderer_context = renderer_context or {} view = renderer_context.get("view", None) request = renderer_context.get("request", None) # Get the resource name. resource_name = utils.get_resource_name(renderer_context) # If this is an error response, skip the rest. if resource_name == 'errors': return self.render_errors(data, accepted_media_type, renderer_context) # if response.status_code is 204 then the data to be rendered must # be None response = renderer_context.get('response', None) if response is not None and response.status_code == 204: return super(JSONRenderer, self).render( None, accepted_media_type, renderer_context ) from rest_framework_json_api.views import RelationshipView if isinstance(view, RelationshipView): return self.render_relationship_view(data, accepted_media_type, renderer_context) # If `resource_name` is set to None then render default as the dev # wants to build the output format manually. if resource_name is None or resource_name is False: return super(JSONRenderer, self).render( data, accepted_media_type, renderer_context ) json_api_data = data # initialize json_api_meta with pagination meta or an empty dict json_api_meta = data.get('meta', {}) if isinstance(data, dict) else {} included_cache = defaultdict(dict) if data and 'results' in data: serializer_data = data["results"] else: serializer_data = data serializer = getattr(serializer_data, 'serializer', None) included_resources = utils.get_included_resources(request, serializer) if serializer is not None: # Extract root meta for any type of serializer json_api_meta.update(self.extract_root_meta(serializer, serializer_data)) if getattr(serializer, 'many', False): json_api_data = list() for position in range(len(serializer_data)): resource = serializer_data[position] # Get current resource resource_instance = serializer.instance[position] # Get current instance if isinstance(serializer.child, rest_framework_json_api. serializers.PolymorphicModelSerializer): resource_serializer_class = serializer.child.\ get_polymorphic_serializer_for_instance(resource_instance)( context=serializer.child.context ) else: resource_serializer_class = serializer.child fields = utils.get_serializer_fields(resource_serializer_class) force_type_resolution = getattr( resource_serializer_class, '_poly_force_type_resolution', False) json_resource_obj = self.build_json_resource_obj( fields, resource, resource_instance, resource_name, force_type_resolution ) meta = self.extract_meta(serializer, resource) if meta: json_resource_obj.update({'meta': utils._format_object(meta)}) json_api_data.append(json_resource_obj) self.extract_included( fields, resource, resource_instance, included_resources, included_cache ) else: fields = utils.get_serializer_fields(serializer) force_type_resolution = getattr(serializer, '_poly_force_type_resolution', False) resource_instance = serializer.instance json_api_data = self.build_json_resource_obj( fields, serializer_data, resource_instance, resource_name, force_type_resolution ) meta = self.extract_meta(serializer, serializer_data) if meta: json_api_data.update({'meta': utils._format_object(meta)}) self.extract_included( fields, serializer_data, resource_instance, included_resources, included_cache ) # Make sure we render data in a specific order render_data = OrderedDict() if isinstance(data, dict) and data.get('links'): render_data['links'] = data.get('links') # format the api root link list if view.__class__ and view.__class__.__name__ == 'APIRoot': render_data['data'] = None render_data['links'] = json_api_data else: render_data['data'] = json_api_data if included_cache: if isinstance(json_api_data, list): objects = json_api_data else: objects = [json_api_data] for object in objects: obj_type = object.get('type') obj_id = object.get('id') if obj_type in included_cache and \ obj_id in included_cache[obj_type]: del included_cache[obj_type][obj_id] if not included_cache[obj_type]: del included_cache[obj_type] if included_cache: render_data['included'] = list() for included_type in sorted(included_cache.keys()): for included_id in sorted(included_cache[included_type].keys()): render_data['included'].append(included_cache[included_type][included_id]) if json_api_meta: render_data['meta'] = utils._format_object(json_api_meta) return super(JSONRenderer, self).render( render_data, accepted_media_type, renderer_context )
def test_get_resource_name_no_view(): assert get_resource_name({}) is None
def test_get_resource_name_with_errors(status_code): view = APIView() context = {"view": view} view.response = Response(status=status_code) assert "errors" == get_resource_name(context)
def render(self, data, accepted_media_type=None, renderer_context=None): renderer_context = renderer_context or {} view = renderer_context.get("view", None) request = renderer_context.get("request", None) # Get the resource name. resource_name = utils.get_resource_name(renderer_context) # If this is an error response, skip the rest. if resource_name == 'errors': return self.render_errors(data, accepted_media_type, renderer_context) # if response.status_code is 204 then the data to be rendered must # be None response = renderer_context.get('response', None) if response is not None and response.status_code == 204: return super(JSONRenderer, self).render( None, accepted_media_type, renderer_context ) from rest_framework_json_api.views import RelationshipView if isinstance(view, RelationshipView): return self.render_relationship_view(data, accepted_media_type, renderer_context) # If `resource_name` is set to None then render default as the dev # wants to build the output format manually. if resource_name is None or resource_name is False: return super(JSONRenderer, self).render( data, accepted_media_type, renderer_context ) json_api_data = data # initialize json_api_meta with pagination meta or an empty dict json_api_meta = data.get('meta', {}) if isinstance(data, dict) else {} included_cache = defaultdict(dict) if data and 'results' in data: serializer_data = data["results"] else: serializer_data = data serializer = getattr(serializer_data, 'serializer', None) included_resources = utils.get_included_resources(request, serializer) if serializer is not None: # Extract root meta for any type of serializer json_api_meta.update(self.extract_root_meta(serializer, serializer_data)) if getattr(serializer, 'many', False): json_api_data = list() for position in range(len(serializer_data)): resource = serializer_data[position] # Get current resource resource_instance = serializer.instance[position] # Get current instance if isinstance(serializer.child, rest_framework_json_api. serializers.PolymorphicModelSerializer): resource_serializer_class = serializer.child.\ get_polymorphic_serializer_for_instance(resource_instance)() else: resource_serializer_class = serializer.child fields = utils.get_serializer_fields(resource_serializer_class) force_type_resolution = getattr( resource_serializer_class, '_poly_force_type_resolution', False) json_resource_obj = self.build_json_resource_obj( fields, resource, resource_instance, resource_name, force_type_resolution ) meta = self.extract_meta(serializer, resource) if meta: json_resource_obj.update({'meta': utils.format_keys(meta)}) json_api_data.append(json_resource_obj) self.extract_included( fields, resource, resource_instance, included_resources, included_cache ) else: fields = utils.get_serializer_fields(serializer) force_type_resolution = getattr(serializer, '_poly_force_type_resolution', False) resource_instance = serializer.instance json_api_data = self.build_json_resource_obj( fields, serializer_data, resource_instance, resource_name, force_type_resolution ) meta = self.extract_meta(serializer, serializer_data) if meta: json_api_data.update({'meta': utils.format_keys(meta)}) self.extract_included( fields, serializer_data, resource_instance, included_resources, included_cache ) # Make sure we render data in a specific order render_data = OrderedDict() if isinstance(data, dict) and data.get('links'): render_data['links'] = data.get('links') # format the api root link list if view.__class__ and view.__class__.__name__ == 'APIRoot': render_data['data'] = None render_data['links'] = json_api_data else: render_data['data'] = json_api_data if included_cache: render_data['included'] = list() for included_type in sorted(included_cache.keys()): for included_id in sorted(included_cache[included_type].keys()): render_data['included'].append(included_cache[included_type][included_id]) if json_api_meta: render_data['meta'] = utils.format_keys(json_api_meta) return super(JSONRenderer, self).render( render_data, accepted_media_type, renderer_context )
def render(self, data, accepted_media_type=None, renderer_context=None): view = renderer_context.get('view', None) request = renderer_context.get('request', None) # Get the resource name. resource_name = utils.get_resource_name(renderer_context) # If this is an error response, skip the rest. if resource_name == 'errors': return self.render_errors(data, accepted_media_type, renderer_context) from rest_framework_json_api.views import RelationshipView if isinstance(view, RelationshipView): return self.render_relationship_view(data, accepted_media_type, renderer_context) # If `resource_name` is set to None then render default as the dev # wants to build the output format manually. if resource_name is None or resource_name is False: return super().render(data, accepted_media_type, renderer_context) json_api_data = data json_api_included = list() # initialize json_api_meta with pagination meta or an empty dict json_api_meta = data.get('meta', {}) if isinstance(data, dict) else {} if data and 'results' in data: serializer_data = data['results'] else: serializer_data = data serializer = getattr(serializer_data, 'serializer', None) included_resources = utils.get_included_resources(request, serializer) if serializer is not None: # Get the serializer fields fields = utils.get_serializer_fields(serializer) # Extract root meta for any type of serializer json_api_meta.update( self.extract_root_meta(serializer, serializer_data)) if getattr(serializer, 'many', False): json_api_data = list() for position in range(len(serializer_data)): resource = serializer_data[ position] # Get current resource resource_instance = serializer.instance[ position] # Get current instance json_resource_obj = self.build_json_resource_obj( fields, resource, resource_instance, resource_name) meta = self.extract_meta(serializer, resource) if meta: json_resource_obj.update( {'meta': api_utils.format_keys(meta)}) json_api_data.append(json_resource_obj) included = self.extract_included(fields, resource, resource_instance, included_resources) if included: json_api_included.extend(included) else: resource_instance = serializer.instance json_api_data = self.build_json_resource_obj( fields, serializer_data, resource_instance, resource_name) meta = self.extract_meta(serializer, serializer_data) if meta: json_api_data.update({'meta': api_utils.format_keys(meta)}) included = self.extract_included(fields, serializer_data, resource_instance, included_resources) if included: json_api_included.extend(included) # Make sure we render data in a specific order render_data = OrderedDict() if isinstance(data, dict) and data.get('links'): render_data['links'] = data.get('links') from rest_framework.routers import APIRootView # format the api root link list if isinstance(view, APIRootView): render_data['data'] = None render_data['links'] = json_api_data else: render_data['data'] = json_api_data if len(json_api_included) > 0: # Iterate through compound documents to remove duplicates seen = set() unique_compound_documents = list() for included_dict in json_api_included: type_tuple = tuple( (included_dict['type'], included_dict['id'])) if type_tuple not in seen: seen.add(type_tuple) unique_compound_documents.append(included_dict) # Sort the items by type then by id render_data['included'] = sorted(unique_compound_documents, key=lambda item: (item['type'], item['id'])) if json_api_meta: render_data['meta'] = api_utils.format_keys(json_api_meta) return super().render(render_data, accepted_media_type, renderer_context)
def render(self, data, accepted_media_type=None, renderer_context=None): view = renderer_context.get("view", None) request = renderer_context.get("request", None) # Get the resource name. resource_name = utils.get_resource_name(renderer_context) # If this is an error response, skip the rest. if resource_name == 'errors': return self.render_errors(data, accepted_media_type, renderer_context) # if response.status_code is 204 then the data to be rendered must # be None response = renderer_context.get('response', None) if response is not None and response.status_code == 204: return super(JSONRenderer, self).render(None, accepted_media_type, renderer_context) from rest_framework_json_api.views import RelationshipView if isinstance(view, RelationshipView): return self.render_relationship_view(data, accepted_media_type, renderer_context) # If `resource_name` is set to None then render default as the dev # wants to build the output format manually. if resource_name is None or resource_name is False: return super(JSONRenderer, self).render(data, accepted_media_type, renderer_context) json_api_data = data json_api_included = list() # initialize json_api_meta with pagination meta or an empty dict json_api_meta = data.get('meta', {}) if isinstance(data, dict) else {} if json_api_meta: meta_pagination = json_api_meta.pop('pagination') meta_links = json_api_meta.pop('links') meta_pagination.update(meta_links) json_api_meta['pagination'] = meta_pagination json_api_meta['include'] = [] json_api_meta['custom'] = [] if data and 'results' in data: serializer_data = data["results"] else: serializer_data = data serializer = getattr(serializer_data, 'serializer', None) included_resources = utils.get_included_resources(request, serializer) if serializer is not None: # Get the serializer fields fields = utils.get_serializer_fields(serializer) # Determine if resource name must be resolved on each instance (polymorphic serializer) force_type_resolution = getattr(serializer, '_poly_force_type_resolution', False) # Extract root meta for any type of serializer json_api_meta.update( self.extract_root_meta(serializer, serializer_data)) if getattr(serializer, 'many', False): json_api_data = list() for position in range(len(serializer_data)): resource = serializer_data[ position] # Get current resource resource_instance = serializer.instance[ position] # Get current instance json_resource_obj = self.build_json_resource_obj( fields, resource, resource_instance, resource_name, force_type_resolution) meta = self.extract_meta(serializer, resource) if meta: json_resource_obj.update( {'meta': utils.format_keys(meta)}) json_api_data.append(json_resource_obj) included = self.extract_included(fields, resource, resource_instance, included_resources) if included: json_api_included.extend(included) else: resource_instance = serializer.instance json_api_data = self.build_json_resource_obj( fields, serializer_data, resource_instance, resource_name, force_type_resolution) meta = self.extract_meta(serializer, serializer_data) if meta: json_api_data.update({'meta': utils.format_keys(meta)}) included = self.extract_included(fields, serializer_data, resource_instance, included_resources) if included: json_api_included.extend(included) # Make sure we render data in a specific order render_data = OrderedDict() if isinstance(data, dict) and data.get('links'): render_data['links'] = data.get('links') # format the api root link list if view.__class__ and view.__class__.__name__ == 'APIRoot': render_data['data'] = None render_data['links'] = json_api_data else: render_data['data'] = json_api_data if len(json_api_included) > 0: # Iterate through compound documents to remove duplicates seen = set() unique_compound_documents = list() for included_dict in json_api_included: type_tuple = tuple( (included_dict['type'], included_dict['id'])) if type_tuple not in seen: seen.add(type_tuple) unique_compound_documents.append(included_dict) # Sort the items by type then by id render_data['included'] = sorted(unique_compound_documents, key=lambda item: (item['type'], item['id'])) if json_api_meta: render_data['meta'] = utils.format_keys(json_api_meta) return super(renderers.JSONRenderer, self).render(render_data, accepted_media_type, renderer_context)
def parse(self, stream, media_type=None, parser_context=None): """Slightly modified to also check DELETE request body data _if_ provided.""" result = super(parsers.JSONParser, self).parse( stream, media_type=media_type, parser_context=parser_context ) if not isinstance(result, dict) or "data" not in result: raise ParseError("Received document does not contain primary data") data = result.get("data") view = parser_context["view"] from rest_framework_json_api.views import RelationshipView if isinstance(view, RelationshipView): # We skip parsing the object as JSONAPI Resource Identifier Object and not a regular # Resource Object if isinstance(data, list): for resource_identifier_object in data: if not ( resource_identifier_object.get("id") and resource_identifier_object.get("type") ): raise ParseError( "Received data contains one or more malformed JSONAPI " "Resource Identifier Object(s)" ) elif not (data.get("id") and data.get("type")): raise ParseError( "Received data is not a valid JSONAPI Resource Identifier Object" ) return data request = parser_context.get("request") # Sanity check if not isinstance(data, dict): raise ParseError( "Received data is not a valid JSONAPI Resource Identifier Object" ) # Check for inconsistencies if request.method in ("PUT", "POST", "PATCH", "DELETE"): resource_name = utils.get_resource_name( parser_context, expand_polymorphic_types=True ) if isinstance(resource_name, str): if data.get("type") != resource_name: raise exceptions.Conflict( "The resource object's type ({data_type}) is not the type that " "constitute the collection represented by the endpoint " "({resource_type}).".format( data_type=data.get("type"), resource_type=resource_name ) ) else: if data.get("type") not in resource_name: raise exceptions.Conflict( "The resource object's type ({data_type}) is not the type that " "constitute the collection represented by the endpoint " "(one of [{resource_types}]).".format( data_type=data.get("type"), resource_types=", ".join(resource_name), ) ) if not data.get("id") and request.method in ("PATCH", "PUT", "DELETE"): raise ParseError( "The resource identifier object must contain an 'id' member" ) if request.method in ("PATCH", "PUT", "DELETE"): lookup_url_kwarg = getattr(view, "lookup_url_kwarg", None) or getattr( view, "lookup_field", None ) if lookup_url_kwarg and 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[lookup_url_kwarg] ) ) # Construct the return data serializer_class = getattr(view, "serializer_class", None) parsed_data = {"id": data.get("id")} if "id" in data else {} # `type` field needs to be allowed in none polymorphic serializers if serializer_class is not None: if issubclass(serializer_class, serializers.PolymorphicModelSerializer): parsed_data["type"] = data.get("type") parsed_data.update(self.parse_attributes(data)) parsed_data.update(self.parse_relationships(data)) parsed_data.update(self.parse_metadata(result)) return parsed_data