def __init__(self, *args, **kwargs): context = kwargs.get('context') request = context.get('request') if context else None view = context.get('view') if context else None def validate_path(serializer_class, field_path, path): serializers = get_included_serializers(serializer_class) if serializers is None: raise ParseError( 'This endpoint does not support the include parameter') this_field_name = inflection.underscore(field_path[0]) this_included_serializer = serializers.get(this_field_name) if this_included_serializer is None: raise ParseError( 'This endpoint does not support the include parameter for path {}' .format(path)) if len(field_path) > 1: new_included_field_path = field_path[1:] # We go down one level in the path validate_path(this_included_serializer, new_included_field_path, path) if request and view: included_resources = get_included_resources(request) for included_field_name in included_resources: included_field_path = included_field_name.split('.') this_serializer_class = view.get_serializer_class() # lets validate the current path validate_path(this_serializer_class, included_field_path, included_field_name) super(IncludedResourcesValidationMixin, self).__init__(*args, **kwargs)
def get_queryset(self, *args, **kwargs): qs = super().get_queryset(*args, **kwargs) included_resources = get_included_resources(self.request) for included in included_resources: included_model = None levels = included.split('.') level_model = qs.model for level in levels: if not hasattr(level_model, level): break field = getattr(level_model, level) field_class = field.__class__ if not (issubclass(field_class, ForwardManyToOneDescriptor) or issubclass(field_class, ManyToManyDescriptor)): break if level == levels[-1]: included_model = field else: level_model = field.get_queryset().model if included_model is not None: qs = qs.prefetch_related(included.replace('.', '__')) return qs
def _get_field_representation(self, field, instance): request = self.context.get('request') is_included = field.source in get_included_resources(request) if not is_included and \ isinstance(field, ModelSerializer) and \ hasattr(instance, field.source + '_id'): attribute = getattr(instance, field.source + '_id') if attribute is None: return None resource_type = get_resource_type_from_serializer(field) if resource_type: return OrderedDict([('type', resource_type), ('id', attribute)]) attribute = field.get_attribute(instance) # When creating the relationships list, we don't need all data from # related models. if isinstance(attribute, (Manager, QuerySet)) and 1: if not getattr(attribute, "_prefetch_done", True): attribute = attribute.only("pk") # We skip `to_representation` for `None` values so that fields do # not have to explicitly deal with that case. # # For related fields with `use_pk_only_optimization` we need to # resolve the pk value. check_for_none = attribute.pk if isinstance( attribute, PKOnlyObject) else attribute if check_for_none is None: return None else: return field.to_representation(attribute)
def get_queryset(self, *args, **kwargs): qs = super().get_queryset(*args, **kwargs) included_resources = get_included_resources(self.request) for included in included_resources: included_model = None levels = included.split('.') level_model = qs.model for level in levels: if not hasattr(level_model, level): break field = getattr(level_model, level) field_class = field.__class__ if not ( issubclass(field_class, ForwardManyToOneDescriptor) or issubclass(field_class, ManyToManyDescriptor) ): break if level == levels[-1]: included_model = field else: level_model = field.get_queryset().model if included_model is not None: qs = qs.prefetch_related(included.replace('.', '__')) return qs
def __init__(self, *args, **kwargs): context = kwargs.get('context') request = context.get('request') if context else None view = context.get('view') if context else None def validate_path(serializer_class, field_path, path): serializers = get_included_serializers(serializer_class) if serializers is None: raise ParseError('This endpoint does not support the include parameter') this_field_name = inflection.underscore(field_path[0]) this_included_serializer = serializers.get(this_field_name) if this_included_serializer is None: raise ParseError( 'This endpoint does not support the include parameter for path {}'.format( path ) ) if len(field_path) > 1: new_included_field_path = field_path[1:] # We go down one level in the path validate_path(this_included_serializer, new_included_field_path, path) if request and view: included_resources = get_included_resources(request) for included_field_name in included_resources: included_field_path = included_field_name.split('.') this_serializer_class = view.get_serializer_class() # lets validate the current path validate_path(this_serializer_class, included_field_path, included_field_name) super(IncludedResourcesValidationMixin, self).__init__(*args, **kwargs)
def _get_field_representation(self, field, instance): request = self.context.get('request') is_included = field.source in get_included_resources(request) if not is_included and \ isinstance(field, ModelSerializer) and \ hasattr(instance, field.source + '_id'): attribute = getattr(instance, field.source + '_id') if attribute is None: return None resource_type = get_resource_type_from_serializer(field) if resource_type: return OrderedDict([('type', resource_type), ('id', attribute)]) attribute = field.get_attribute(instance) # We skip `to_representation` for `None` values so that fields do # not have to explicitly deal with that case. # # For related fields with `use_pk_only_optimization` we need to # resolve the pk value. check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute if check_for_none is None: return None else: return field.to_representation(attribute)
def _get_field_representation(self, field, instance): request = self.context.get('request') is_included = field.source in get_included_resources(request) render_nested_as_attribute = json_api_settings.SERIALIZE_NESTED_SERIALIZERS_AS_ATTRIBUTE if not is_included and \ isinstance(field, ModelSerializer) and \ hasattr(instance, field.source + '_id') and \ not render_nested_as_attribute: attribute = getattr(instance, field.source + '_id') if attribute is None: return None resource_type = get_resource_type_from_serializer(field) if resource_type: return OrderedDict([('type', resource_type), ('id', attribute)]) attribute = field.get_attribute(instance) # We skip `to_representation` for `None` values so that fields do # not have to explicitly deal with that case. # # For related fields with `use_pk_only_optimization` we need to # resolve the pk value. check_for_none = attribute.pk if isinstance( attribute, PKOnlyObject) else attribute if check_for_none is None: return None else: return field.to_representation(attribute)
def get_queryset(self, *args, **kwargs): """ This mixin adds automatic prefetching for OneToOne and ManyToMany fields. """ qs = super(AutoPreloadMixin, self).get_queryset(*args, **kwargs) included_resources = get_included_resources(self.request) for included in included_resources + ['__all__']: # If include was not defined, trying to resolve it automatically included_model = None levels = included.split('.') level_model = qs.model # Suppose we can do select_related by default can_select_related = True for level in levels: if not hasattr(level_model, level): break field = getattr(level_model, level) field_class = field.__class__ is_forward_relation = ( issubclass(field_class, (ForwardManyToOneDescriptor, ManyToManyDescriptor)) ) is_reverse_relation = ( issubclass(field_class, (ReverseManyToOneDescriptor, ReverseOneToOneDescriptor)) ) if not (is_forward_relation or is_reverse_relation): break # Figuring out if relation should be select related rather than prefetch_related # If at least one relation in the chain is not "selectable" then use "prefetch" can_select_related &= ( issubclass(field_class, (ForwardManyToOneDescriptor, ReverseOneToOneDescriptor)) ) if level == levels[-1]: included_model = field else: if issubclass(field_class, ReverseOneToOneDescriptor): model_field = field.related.field else: model_field = field.field if is_forward_relation: level_model = model_field.related_model else: level_model = model_field.model if included_model is not None: if can_select_related: qs = qs.select_related(included.replace('.', '__')) else: qs = qs.prefetch_related(included.replace('.', '__')) return qs
def get_queryset(self, *args, **kwargs): """ This mixin adds automatic prefetching for OneToOne and ManyToMany fields. """ qs = super(AutoPreloadMixin, self).get_queryset(*args, **kwargs) included_resources = get_included_resources(self.request) for included in included_resources + ['__all__']: # If include was not defined, trying to resolve it automatically included_model = None levels = included.split('.') level_model = qs.model # Suppose we can do select_related by default can_select_related = True for level in levels: if not hasattr(level_model, level): break field = getattr(level_model, level) field_class = field.__class__ is_forward_relation = (issubclass( field_class, (ForwardManyToOneDescriptor, ManyToManyDescriptor))) is_reverse_relation = (issubclass( field_class, (ReverseManyToOneDescriptor, ReverseOneToOneDescriptor))) if not (is_forward_relation or is_reverse_relation): break # Figuring out if relation should be select related rather than prefetch_related # If at least one relation in the chain is not "selectable" then use "prefetch" can_select_related &= (issubclass( field_class, (ForwardManyToOneDescriptor, ReverseOneToOneDescriptor))) if level == levels[-1]: included_model = field else: if issubclass(field_class, ReverseOneToOneDescriptor): model_field = field.related.field else: model_field = field.field if is_forward_relation: level_model = model_field.related_model else: level_model = model_field.model if included_model is not None: if can_select_related: qs = qs.select_related(included.replace('.', '__')) else: qs = qs.prefetch_related(included.replace('.', '__')) return qs
def get_queryset(self, *args, **kwargs): qs = super(PreloadIncludesMixin, self).get_queryset(*args, **kwargs) included_resources = get_included_resources(self.request) for included in included_resources + ['__all__']: select_related = self.get_select_related(included) if select_related is not None: qs = qs.select_related(*select_related) prefetch_related = self.get_prefetch_related(included) if prefetch_related is not None: qs = qs.prefetch_related(*prefetch_related) return qs
def get_queryset(self, *args, **kwargs): qs = super().get_queryset(*args, **kwargs) included_resources = get_included_resources( self.request, self.get_serializer_class()) for included in included_resources + ["__all__"]: select_related = self.get_select_related(included) if select_related is not None: qs = qs.select_related(*select_related) prefetch_related = self.get_prefetch_related(included) if prefetch_related is not None: qs = qs.prefetch_related(*prefetch_related) return qs
def get_queryset(self, *args, **kwargs): """This mixin adds automatic prefetching for OneToOne and ManyToMany fields.""" qs = super().get_queryset(*args, **kwargs) included_resources = get_included_resources( self.request, self.get_serializer_class()) for included in included_resources + ["__all__"]: # If include was not defined, trying to resolve it automatically included_model = None levels = included.split(".") level_model = qs.model for level in levels: if not hasattr(level_model, level): break field = getattr(level_model, level) field_class = field.__class__ is_forward_relation = issubclass( field_class, (ForwardManyToOneDescriptor, ManyToManyDescriptor)) is_reverse_relation = issubclass( field_class, (ReverseManyToOneDescriptor, ReverseOneToOneDescriptor)) if not (is_forward_relation or is_reverse_relation): break if level == levels[-1]: included_model = field else: if issubclass(field_class, ReverseOneToOneDescriptor): model_field = field.related.field else: model_field = field.field if is_forward_relation: level_model = model_field.related_model else: level_model = model_field.model if included_model is not None: qs = qs.prefetch_related(included.replace(".", "__")) return qs
def get_queryset(self, *args, **kwargs): """ This mixin adds automatic prefetching for OneToOne and ManyToMany fields. """ qs = super(AutoPrefetchMixin, self).get_queryset(*args, **kwargs) included_resources = get_included_resources(self.request) for included in included_resources: included_model = None levels = included.split('.') level_model = qs.model for level in levels: if not hasattr(level_model, level): break field = getattr(level_model, level) field_class = field.__class__ is_forward_relation = ( issubclass(field_class, ForwardManyToOneDescriptor) or issubclass(field_class, ManyToManyDescriptor) ) is_reverse_relation = ( issubclass(field_class, ReverseManyToOneDescriptor) or issubclass(field_class, ReverseOneToOneDescriptor) ) if not (is_forward_relation or is_reverse_relation): break if level == levels[-1]: included_model = field else: if django.VERSION < (1, 9): model_field = field.related else: model_field = field.field if is_forward_relation: level_model = model_field.related_model else: level_model = model_field.model if included_model is not None: qs = qs.prefetch_related(included.replace('.', '__')) return qs
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) context = kwargs.get("context") request = context.get("request") if context else None data = kwargs.get("data", {}) _fields = list(data.keys()) if request: resource_type = get_resource_type_from_serializer(self) requested_fields = request.query_params.get( f"fields[{resource_type}]") if requested_fields: _fields.extend(requested_fields.split(",")) _fields.extend(get_included_resources(request)) # To manually include certain fields without having to make a http request: include_fields = context.get("include_fields") if context else None if include_fields: _fields.extend(include_fields) for fieldname in getattr(self.Meta, "exclude_by_default", []): if fieldname not in _fields: self.fields.pop(fieldname, None)
def __init__(self, *args, **kwargs): context = kwargs.get("context") request = context.get("request") if context else None view = context.get("view") if context else None def validate_path(serializer_class, field_path, path): serializers = getattr(serializer_class, "included_serializers", None) if serializers is None: raise ParseError( "This endpoint does not support the include parameter") this_field_name = inflection.underscore(field_path[0]) this_included_serializer = serializers.get(this_field_name) if this_included_serializer is None: raise ParseError( "This endpoint does not support the include parameter for path {}" .format(path)) if len(field_path) > 1: new_included_field_path = field_path[1:] # We go down one level in the path validate_path(this_included_serializer, new_included_field_path, path) if request and view: included_resources = get_included_resources(request) for included_field_name in included_resources: included_field_path = included_field_name.split(".") if "related_field" in view.kwargs: this_serializer_class = view.get_related_serializer_class() else: this_serializer_class = view.get_serializer_class() # lets validate the current path validate_path(this_serializer_class, included_field_path, included_field_name) super().__init__(*args, **kwargs)
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): 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): 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 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): 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)