Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
    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)
Exemplo n.º 7
0
    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
Exemplo n.º 9
0
    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
Exemplo n.º 11
0
    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
Exemplo n.º 12
0
    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
Exemplo n.º 13
0
    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
Exemplo n.º 15
0
 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)
Exemplo n.º 16
0
    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)
Exemplo n.º 17
0
    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)
Exemplo n.º 18
0
    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)
Exemplo n.º 21
0
    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)