def get_field_info(self, field):
        """
        Given an instance of a serializer field, return a dictionary
        of metadata about it.
        """
        field_info = OrderedDict()
        serializer = field.parent

        if isinstance(field, serializers.ManyRelatedField):
            field_info['type'] = self.type_lookup[field.child_relation]
        else:
            field_info['type'] = self.type_lookup[field]

        try:
            serializer_model = getattr(serializer.Meta, 'model')
            field_info['relationship_type'] = self.relation_type_lookup[
                getattr(serializer_model, field.field_name)
            ]
        except KeyError:
            pass
        except AttributeError:
            pass
        else:
            field_info['relationship_resource'] = get_related_resource_type(field)

        field_info['required'] = getattr(field, 'required', False)

        attrs = [
            'read_only', 'write_only', 'label', 'help_text',
            'min_length', 'max_length',
            'min_value', 'max_value', 'initial'
        ]

        for attr in attrs:
            value = getattr(field, attr, None)
            if value is not None and value != '':
                field_info[attr] = force_text(value, strings_only=True)

        if getattr(field, 'child', None):
            field_info['child'] = self.get_field_info(field.child)
        elif getattr(field, 'fields', None):
            field_info['children'] = self.get_serializer_info(field)

        if (
            not field_info.get('read_only') and
            not field_info.get('relationship_resource') and
            hasattr(field, 'choices')
        ):
            field_info['choices'] = [
                {
                    'value': choice_value,
                    'display_name': force_text(choice_name, strings_only=True)
                }
                for choice_value, choice_name in field.choices.items()
            ]

        if hasattr(serializer, 'included_serializers') and 'relationship_resource' in field_info:
            field_info['allows_include'] = field.field_name in serializer.included_serializers

        return field_info
    def test_m2m_relation(self):
        """
        Ensure m2ms have their types identified correctly.
        """
        serializer = EntrySerializer()
        field = serializer.fields['authors']

        self.assertEqual(utils.get_related_resource_type(field), 'authors')
    def test_reverse_relation(self):
        """
        Ensure reverse foreign keys have their types identified correctly.
        """
        serializer = EntrySerializer()
        field = serializer.fields['comments']

        self.assertEqual(utils.get_related_resource_type(field), 'comments')
Exemple #4
0
    def test_m2m_relation(self):
        """
        Ensure m2ms have their types identified correctly.
        """
        serializer = EntrySerializer()
        field = serializer.fields['authors']

        self.assertEqual(utils.get_related_resource_type(field), 'authors')
Exemple #5
0
    def test_reverse_relation(self):
        """
        Ensure reverse foreign keys have their types identified correctly.
        """
        serializer = EntrySerializer()
        field = serializer.fields['comments']

        self.assertEqual(utils.get_related_resource_type(field), 'comments')
    def get_field_info(self, field):
        """
        Given an instance of a serializer field, return a dictionary
        of metadata about it.
        """
        field_info = OrderedDict()
        serializer = field.parent

        if isinstance(field, serializers.ManyRelatedField):
            field_info['type'] = self.type_lookup[field.child_relation]
        else:
            field_info['type'] = self.type_lookup[field]

        try:
            serializer_model = getattr(serializer.Meta, 'model')
            field_info['relationship_type'] = self.relation_type_lookup[
                getattr(serializer_model, field.field_name)]
        except KeyError:
            pass
        except AttributeError:
            pass
        else:
            field_info['relationship_resource'] = get_related_resource_type(
                field)

        field_info['required'] = getattr(field, 'required', False)

        attrs = [
            'read_only', 'write_only', 'label', 'help_text', 'min_length',
            'max_length', 'min_value', 'max_value', 'initial'
        ]

        for attr in attrs:
            value = getattr(field, attr, None)
            if value is not None and value != '':
                field_info[attr] = force_text(value, strings_only=True)

        if getattr(field, 'child', None):
            field_info['child'] = self.get_field_info(field.child)
        elif getattr(field, 'fields', None):
            field_info['children'] = self.get_serializer_info(field)

        if (not field_info.get('read_only')
                and not field_info.get('relationship_resource')
                and hasattr(field, 'choices')):
            field_info['choices'] = [{
                'value':
                choice_value,
                'display_name':
                force_text(choice_name, strings_only=True)
            } for choice_value, choice_name in field.choices.items()]

        if hasattr(serializer, 'included_serializers'
                   ) and 'relationship_resource' in field_info:
            field_info[
                'allows_include'] = field.field_name in serializer.included_serializers

        return field_info
Exemple #7
0
def test_get_related_resource_type(model_class, field, output):
    class RelatedResourceTypeSerializer(serializers.ModelSerializer):
        class Meta:
            model = model_class
            fields = (field,)

    serializer = RelatedResourceTypeSerializer()
    field = serializer.fields[field]
    assert get_related_resource_type(field) == output
def test_get_related_resource_type_from_plain_serializer_class(
        related_field_kwargs, output):
    class PlainRelatedResourceTypeSerializer(serializers.Serializer):
        basic_models = serializers.ResourceRelatedField(many=True,
                                                        **related_field_kwargs)

    serializer = PlainRelatedResourceTypeSerializer()
    field = serializer.fields["basic_models"]
    assert get_related_resource_type(field) == output
Exemple #9
0
 def generate_relationship(self, field):
     field_schema = openapi.Schema(
         title='Relationship object',
         type=openapi.TYPE_OBJECT,
         properties=utils.OrderedDict((
             ('type', openapi.Schema(
                 type=openapi.TYPE_STRING,
                 title='Type of related object',
                 enum=[get_related_resource_type(field)]
             )),
             ('id', openapi.Schema(
                 type=openapi.TYPE_STRING,
                 title='ID of related object',
             ))
         ))
     )
     return field.field_name, self.decorate_with_data(field_schema)
    def extract_relationships(cls, fields, resource, resource_instance):
        """
        Builds the relationships top level object based on related serializers.
        """
        # Avoid circular deps
        from rest_framework_json_api.relations import ResourceRelatedField

        data = OrderedDict()

        # Don't try to extract relationships from a non-existent resource
        if resource_instance is None:
            return

        for field_name, field in iter(fields.items()):
            # Skip URL field
            if field_name == api_settings.URL_FIELD_NAME:
                continue

            # don't output a key for write only fields
            if fields[field_name].write_only:
                continue

            # Skip fields without relations
            if not utils.is_relationship_field(field):
                continue

            source = field.source
            relation_type = utils.get_related_resource_type(field)

            if isinstance(field, relations.HyperlinkedIdentityField):
                resolved, relation_instance = utils.get_relation_instance(
                    resource_instance, source, field.parent)
                if not resolved:
                    continue
                # special case for HyperlinkedIdentityField
                relation_data = list()

                # Don't try to query an empty relation
                relation_queryset = (relation_instance if relation_instance
                                     is not None else list())

                for related_object in relation_queryset:
                    relation_data.append(
                        OrderedDict([
                            ("type", relation_type),
                            ("id", encoding.force_str(related_object.pk)),
                        ]))

                data.update({
                    field_name: {
                        "links": {
                            "related": resource.get(field_name)
                        },
                        "data": relation_data,
                        "meta": {
                            "count": len(relation_data)
                        },
                    }
                })
                continue

            relation_data = {}
            if isinstance(field, HyperlinkedMixin):
                field_links = field.get_links(resource_instance,
                                              field.related_link_lookup_field)
                relation_data.update(
                    {"links": field_links} if field_links else dict())
                data.update({field_name: relation_data})

            if isinstance(field, (ResourceRelatedField, )):
                if not isinstance(field, SkipDataMixin):
                    relation_data.update({"data": resource.get(field_name)})

                data.update({field_name: relation_data})
                continue

            if isinstance(
                    field,
                (relations.PrimaryKeyRelatedField,
                 relations.HyperlinkedRelatedField),
            ):
                resolved, relation = utils.get_relation_instance(
                    resource_instance, "%s_id" % source, field.parent)
                if not resolved:
                    continue
                relation_id = relation if resource.get(field_name) else None
                relation_data = {
                    "data": (OrderedDict([
                        ("type", relation_type),
                        ("id", encoding.force_str(relation_id)),
                    ]) if relation_id is not None else None)
                }

                if isinstance(field, relations.HyperlinkedRelatedField
                              ) and resource.get(field_name):
                    relation_data.update(
                        {"links": {
                            "related": resource.get(field_name)
                        }})
                data.update({field_name: relation_data})
                continue

            if isinstance(field, relations.ManyRelatedField):
                resolved, relation_instance = utils.get_relation_instance(
                    resource_instance, source, field.parent)
                if not resolved:
                    continue

                relation_data = {}

                if isinstance(resource.get(field_name), Iterable):
                    relation_data.update(
                        {"meta": {
                            "count": len(resource.get(field_name))
                        }})

                if isinstance(field.child_relation, ResourceRelatedField):
                    # special case for ResourceRelatedField
                    relation_data.update({"data": resource.get(field_name)})

                if isinstance(field.child_relation, HyperlinkedMixin):
                    field_links = field.child_relation.get_links(
                        resource_instance,
                        field.child_relation.related_link_lookup_field,
                    )
                    relation_data.update(
                        {"links": field_links} if field_links else dict())

                    data.update({field_name: relation_data})
                    continue

                relation_data = list()
                for nested_resource_instance in relation_instance:
                    nested_resource_instance_type = (
                        relation_type or utils.get_resource_type_from_instance(
                            nested_resource_instance))

                    relation_data.append(
                        OrderedDict([
                            ("type", nested_resource_instance_type),
                            ("id",
                             encoding.force_str(nested_resource_instance.pk)),
                        ]))
                data.update({
                    field_name: {
                        "data": relation_data,
                        "meta": {
                            "count": len(relation_data)
                        },
                    }
                })
                continue

        return utils.format_field_names(data)
Exemple #11
0
    def extract_relationships(cls, fields, resource, resource_instance):
        """
        Builds the relationships top level object based on related serializers.
        """
        # Avoid circular deps
        from rest_framework_json_api.relations import ResourceRelatedField

        data = OrderedDict()
        render_nested_as_attribute = json_api_settings.SERIALIZE_NESTED_SERIALIZERS_AS_ATTRIBUTE

        # Don't try to extract relationships from a non-existent resource
        if resource_instance is None:
            return

        for field_name, field in iter(fields.items()):
            # Skip URL field
            if field_name == api_settings.URL_FIELD_NAME:
                continue

            # don't output a key for write only fields
            if fields[field_name].write_only:
                continue

            # Skip fields without relations
            if not isinstance(field,
                              (relations.RelatedField,
                               relations.ManyRelatedField, BaseSerializer)):
                continue

            if isinstance(field,
                          BaseSerializer) and render_nested_as_attribute:
                continue

            source = field.source
            relation_type = utils.get_related_resource_type(field)

            if isinstance(field, relations.HyperlinkedIdentityField):
                resolved, relation_instance = utils.get_relation_instance(
                    resource_instance, source, field.parent)
                if not resolved:
                    continue
                # special case for HyperlinkedIdentityField
                relation_data = list()

                # Don't try to query an empty relation
                relation_queryset = relation_instance \
                    if relation_instance is not None else list()

                for related_object in relation_queryset:
                    relation_data.append(
                        OrderedDict([('type', relation_type),
                                     ('id',
                                      encoding.force_str(related_object.pk))]))

                data.update({
                    field_name: {
                        'links': {
                            "related": resource.get(field_name)
                        },
                        'data': relation_data,
                        'meta': {
                            'count': len(relation_data)
                        }
                    }
                })
                continue

            relation_data = {}
            if isinstance(field, HyperlinkedMixin):
                field_links = field.get_links(resource_instance,
                                              field.related_link_lookup_field)
                relation_data.update(
                    {'links': field_links} if field_links else dict())
                data.update({field_name: relation_data})

            if isinstance(field, (ResourceRelatedField, )):
                if not isinstance(field, SkipDataMixin):
                    relation_data.update({'data': resource.get(field_name)})

                data.update({field_name: relation_data})
                continue

            if isinstance(field, (relations.PrimaryKeyRelatedField,
                                  relations.HyperlinkedRelatedField)):
                resolved, relation = utils.get_relation_instance(
                    resource_instance, '%s_id' % source, field.parent)
                if not resolved:
                    continue
                relation_id = relation if resource.get(field_name) else None
                relation_data = {
                    'data':
                    (OrderedDict([('type', relation_type),
                                  ('id', encoding.force_str(relation_id))])
                     if relation_id is not None else None)
                }

                if (isinstance(field, relations.HyperlinkedRelatedField)
                        and resource.get(field_name)):
                    relation_data.update(
                        {'links': {
                            'related': resource.get(field_name)
                        }})
                data.update({field_name: relation_data})
                continue

            if isinstance(field, relations.ManyRelatedField):
                resolved, relation_instance = utils.get_relation_instance(
                    resource_instance, source, field.parent)
                if not resolved:
                    continue

                relation_data = {}

                if isinstance(resource.get(field_name), Iterable):
                    relation_data.update(
                        {'meta': {
                            'count': len(resource.get(field_name))
                        }})

                if isinstance(field.child_relation, ResourceRelatedField):
                    # special case for ResourceRelatedField
                    relation_data.update({'data': resource.get(field_name)})

                if isinstance(field.child_relation, HyperlinkedMixin):
                    field_links = field.child_relation.get_links(
                        resource_instance,
                        field.child_relation.related_link_lookup_field)
                    relation_data.update(
                        {'links': field_links} if field_links else dict())

                    data.update({field_name: relation_data})
                    continue

                relation_data = list()
                for nested_resource_instance in relation_instance:
                    nested_resource_instance_type = (
                        relation_type or utils.get_resource_type_from_instance(
                            nested_resource_instance))

                    relation_data.append(
                        OrderedDict([
                            ('type', nested_resource_instance_type),
                            ('id',
                             encoding.force_str(nested_resource_instance.pk))
                        ]))
                data.update({
                    field_name: {
                        'data': relation_data,
                        'meta': {
                            'count': len(relation_data)
                        }
                    }
                })
                continue

            if isinstance(field, ListSerializer):
                resolved, relation_instance = utils.get_relation_instance(
                    resource_instance, source, field.parent)
                if not resolved:
                    continue

                relation_data = list()

                serializer_data = resource.get(field_name)
                resource_instance_queryset = list(relation_instance)
                if isinstance(serializer_data, list):
                    for position in range(len(serializer_data)):
                        nested_resource_instance = resource_instance_queryset[
                            position]
                        nested_resource_instance_type = (
                            relation_type
                            or utils.get_resource_type_from_instance(
                                nested_resource_instance))

                        relation_data.append(
                            OrderedDict([('type',
                                          nested_resource_instance_type),
                                         ('id',
                                          encoding.force_str(
                                              nested_resource_instance.pk))]))

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

            if isinstance(field, Serializer):
                relation_instance_id = getattr(resource_instance,
                                               source + "_id", None)
                if not relation_instance_id:
                    resolved, relation_instance = utils.get_relation_instance(
                        resource_instance, source, field.parent)
                    if not resolved:
                        continue

                    if relation_instance is not None:
                        relation_instance_id = relation_instance.pk

                data.update({
                    field_name: {
                        'data': (OrderedDict([
                            ('type', relation_type),
                            ('id', encoding.force_str(relation_instance_id))
                        ]) if resource.get(field_name) else None)
                    }
                })
                continue

        return utils.format_field_names(data)
Exemple #12
0
    def extract_relationships(cls, fields, resource, resource_instance):
        # Avoid circular deps
        from rest_framework_json_api.relations import ResourceRelatedField

        data = OrderedDict()

        # Don't try to extract relationships from a non-existent resource
        if resource_instance is None:
            return

        for field_name, field in six.iteritems(fields):
            # Skip URL field
            if field_name == api_settings.URL_FIELD_NAME:
                continue

            # Skip fields without relations
            if not isinstance(field,
                              (relations.RelatedField,
                               relations.ManyRelatedField, BaseSerializer)):
                continue

            source = field.source
            relation_type = utils.get_related_resource_type(field)

            if isinstance(field, relations.HyperlinkedIdentityField):
                resolved, relation_instance = utils.get_relation_instance(
                    resource_instance, source, field.parent)
                if not resolved:
                    continue
                # special case for HyperlinkedIdentityField
                relation_data = list()

                # Don't try to query an empty relation
                relation_queryset = relation_instance \
                    if relation_instance is not None else list()

                for related_object in relation_queryset:
                    relation_data.append(
                        OrderedDict([
                            ('type', relation_type),
                            ('id', encoding.force_text(related_object.pk))
                        ]))

                data.update({
                    field_name: {
                        'links': {
                            'related': resource.get(field_name)
                        },
                        'data': cls.encode_ids(relation_data),
                        'meta': {
                            'count': len(relation_data)
                        }
                    }
                })
                continue

            if isinstance(field, ResourceRelatedField):
                resolved, relation_instance = utils.get_relation_instance(
                    resource_instance, source, field.parent)
                if not resolved:
                    continue

                # special case for ResourceRelatedField
                relation_data = {
                    'data': cls.encode_ids(resource.get(field_name))
                }

                field_links = field.get_links(resource_instance)
                relation_data.update(
                    {'links': field_links} if field_links else dict())
                data.update({field_name: relation_data})
                continue

            if isinstance(field, (relations.PrimaryKeyRelatedField,
                                  relations.HyperlinkedRelatedField)):
                resolved, relation = utils.get_relation_instance(
                    resource_instance, '%s_id' % source, field.parent)
                if not resolved:
                    continue
                relation_id = relation if resource.get(field_name) else None
                relation_data = {
                    'data': (OrderedDict([
                        ('type', relation_type),
                        ('id', cls.encode_id(relation_id, relation_type))
                    ]) if relation_id is not None else None)
                }

                relation_data.update(
                    {'links': {
                        'related': resource.get(field_name)
                    }} if isinstance(field, relations.HyperlinkedRelatedField)
                    and resource.get(field_name) else dict())
                data.update({field_name: relation_data})
                continue

            if isinstance(field, relations.ManyRelatedField):
                resolved, relation_instance = utils.get_relation_instance(
                    resource_instance, source, field.parent)
                if not resolved:
                    continue

                if isinstance(field.child_relation, ResourceRelatedField):
                    # special case for ResourceRelatedField
                    relation_data = {
                        'data': cls.encode_ids(resource.get(field_name))
                    }

                    field_links = field.child_relation.get_links(
                        resource_instance)
                    relation_data.update(
                        {'links': field_links} if field_links else dict())
                    relation_data.update(
                        {'meta': {
                            'count': len(resource.get(field_name))
                        }})
                    data.update({field_name: relation_data})
                    continue

                relation_data = list()
                for nested_resource_instance in relation_instance:
                    nested_resource_instance_type = (
                        relation_type or utils.get_resource_type_from_instance(
                            nested_resource_instance))

                    relation_data.append(
                        OrderedDict([
                            ('type', nested_resource_instance_type),
                            ('id',
                             encoding.force_text(nested_resource_instance.pk))
                        ]))
                data.update({
                    field_name: {
                        'data': cls.encode_ids(relation_data),
                        'meta': {
                            'count': len(relation_data)
                        }
                    }
                })
                continue

            if isinstance(field, ListSerializer):
                resolved, relation_instance = utils.get_relation_instance(
                    resource_instance, source, field.parent)
                if not resolved:
                    continue

                relation_data = list()

                serializer_data = resource.get(field_name)
                resource_instance_queryset = list(relation_instance)
                if isinstance(serializer_data, list):
                    for position in range(len(serializer_data)):
                        nested_resource_instance = resource_instance_queryset[
                            position]
                        nested_resource_instance_type = (
                            relation_type
                            or utils.get_resource_type_from_instance(
                                nested_resource_instance))

                        relation_data.append(
                            OrderedDict([('type',
                                          nested_resource_instance_type),
                                         ('id',
                                          encoding.force_text(
                                              nested_resource_instance.pk))]))

                    data.update(
                        {field_name: {
                            'data': cls.encode_ids(relation_data)
                        }})
                    continue

            if isinstance(field, Serializer):
                resolved, relation_instance = utils.get_relation_instance(
                    resource_instance, source, field.parent)
                if not resolved:
                    continue

                data.update({
                    field_name: {
                        'data':
                        (OrderedDict([('type', relation_type),
                                      ('id',
                                       cls.encode_id(resource_instance.pk,
                                                     relation_type))])
                         if resource.get(field_name) else None)
                    }
                })
                continue

        return utils.format_keys(data)
Exemple #13
0
    def extract_relationships(cls, fields, resource, resource_instance):
        # Avoid circular deps
        from rest_framework_json_api.relations import ResourceRelatedField

        data = OrderedDict()

        # Don't try to extract relationships from a non-existent resource
        if resource_instance is None:
            return

        for field_name, field in six.iteritems(fields):
            # Skip URL field
            if field_name == api_settings.URL_FIELD_NAME:
                continue

            # Skip fields without relations
            if not isinstance(field, (relations.RelatedField, relations.ManyRelatedField, BaseSerializer)):
                continue

            source = field.source
            relation_type = utils.get_related_resource_type(field)

            if isinstance(field, relations.HyperlinkedIdentityField):
                resolved, relation_instance = utils.get_relation_instance(resource_instance, source, field.parent)
                if not resolved:
                    continue
                # special case for HyperlinkedIdentityField
                relation_data = list()

                # Don't try to query an empty relation
                relation_queryset = relation_instance \
                    if relation_instance is not None else list()

                for related_object in relation_queryset:
                    relation_data.append(
                        OrderedDict([('type', relation_type), ('id', encoding.force_text(related_object.pk))])
                    )

                data.update({field_name: {
                    'links': {
                        'related': resource.get(field_name)},
                    'data': cls.encode_ids(relation_data),
                    'meta': {
                        'count': len(relation_data)
                    }
                }})
                continue

            if isinstance(field, ResourceRelatedField):
                resolved, relation_instance = utils.get_relation_instance(resource_instance, source, field.parent)
                if not resolved:
                    continue

                # special case for ResourceRelatedField
                relation_data = {
                    'data': cls.encode_ids(resource.get(field_name))
                }

                field_links = field.get_links(resource_instance)
                relation_data.update(
                    {'links': field_links}
                    if field_links else dict()
                )
                data.update({field_name: relation_data})
                continue

            if isinstance(field, (relations.PrimaryKeyRelatedField, relations.HyperlinkedRelatedField)):
                resolved, relation = utils.get_relation_instance(resource_instance, '%s_id' % source, field.parent)
                if not resolved:
                    continue
                relation_id = relation if resource.get(field_name) else None
                relation_data = {
                    'data': (
                        OrderedDict([('type', relation_type), ('id', cls.encode_id(relation_id, relation_type))])
                        if relation_id is not None else None)
                }

                relation_data.update(
                    {'links': {'related': resource.get(field_name)}}
                    if isinstance(field, relations.HyperlinkedRelatedField) and resource.get(field_name) else dict()
                )
                data.update({field_name: relation_data})
                continue

            if isinstance(field, relations.ManyRelatedField):
                resolved, relation_instance = utils.get_relation_instance(resource_instance, source, field.parent)
                if not resolved:
                    continue

                if isinstance(field.child_relation, ResourceRelatedField):
                    # special case for ResourceRelatedField
                    relation_data = {
                        'data': cls.encode_ids(resource.get(field_name))
                    }

                    field_links = field.child_relation.get_links(resource_instance)
                    relation_data.update(
                        {'links': field_links}
                        if field_links else dict()
                    )
                    relation_data.update(
                        {
                            'meta': {
                                'count': len(resource.get(field_name))
                            }
                        }
                    )
                    data.update({field_name: relation_data})
                    continue

                relation_data = list()
                for nested_resource_instance in relation_instance:
                    nested_resource_instance_type = (
                        relation_type or
                        utils.get_resource_type_from_instance(nested_resource_instance)
                    )

                    relation_data.append(OrderedDict([
                        ('type', nested_resource_instance_type),
                        ('id', encoding.force_text(nested_resource_instance.pk))
                    ]))
                data.update({
                    field_name: {
                        'data': cls.encode_ids(relation_data),
                        'meta': {
                            'count': len(relation_data)
                        }
                    }
                })
                continue

            if isinstance(field, ListSerializer):
                resolved, relation_instance = utils.get_relation_instance(resource_instance, source, field.parent)
                if not resolved:
                    continue

                relation_data = list()

                serializer_data = resource.get(field_name)
                resource_instance_queryset = list(relation_instance)
                if isinstance(serializer_data, list):
                    for position in range(len(serializer_data)):
                        nested_resource_instance = resource_instance_queryset[position]
                        nested_resource_instance_type = (
                            relation_type or
                            utils.get_resource_type_from_instance(nested_resource_instance)
                        )

                        relation_data.append(OrderedDict([
                            ('type', nested_resource_instance_type),
                            ('id', encoding.force_text(nested_resource_instance.pk))
                        ]))

                    data.update({field_name: {'data': cls.encode_ids(relation_data)}})
                    continue

            if isinstance(field, Serializer):
                resolved, relation_instance = utils.get_relation_instance(resource_instance, source, field.parent)
                if not resolved:
                    continue

                data.update({
                    field_name: {
                        'data': (
                            OrderedDict([
                                ('type', relation_type),
                                ('id', cls.encode_id(resource_instance.pk, relation_type))
                            ]) if resource.get(field_name) else None)
                    }
                })
                continue

        return utils.format_keys(data)
    def extract_relationships(cls, fields, resource, resource_instance):
        """
        Builds the relationships top level object based on related serializers.
        """
        # Avoid circular deps
        from rest_framework_json_api.relations import ResourceRelatedField

        data = OrderedDict()

        # Don't try to extract relationships from a non-existent resource
        if resource_instance is None:
            return

        for field_name, field in six.iteritems(fields):
            # Skip URL field
            if field_name == api_settings.URL_FIELD_NAME:
                continue

            # don't output a key for write only fields
            if fields[field_name].write_only:
                continue

            # Skip fields without relations
            if not isinstance(
                field, (relations.RelatedField, relations.ManyRelatedField, BaseSerializer)
            ):
                continue

            source = field.source
            relation_type = utils.get_related_resource_type(field)

            if isinstance(field, relations.HyperlinkedIdentityField):
                resolved, relation_instance = utils.get_relation_instance(
                    resource_instance, source, field.parent
                )
                if not resolved:
                    continue
                # special case for HyperlinkedIdentityField
                relation_data = list()

                # Don't try to query an empty relation
                relation_queryset = relation_instance \
                    if relation_instance is not None else list()

                for related_object in relation_queryset:
                    relation_data.append(
                        OrderedDict([
                            ('type', relation_type),
                            ('id', encoding.force_text(related_object.pk))
                        ])
                    )

                data.update({field_name: {
                    'links': {
                        "related": resource.get(field_name)},
                    'data': relation_data,
                    'meta': {
                        'count': len(relation_data)
                    }
                }})
                continue

            relation_data = {}
            if isinstance(field, HyperlinkedMixin):
                field_links = field.get_links(resource_instance, field.related_link_lookup_field)
                relation_data.update({'links': field_links} if field_links else dict())
                data.update({field_name: relation_data})

            if isinstance(field, (ResourceRelatedField, )):
                if not isinstance(field, SkipDataMixin):
                    relation_data.update({'data': resource.get(field_name)})

                data.update({field_name: relation_data})
                continue

            if isinstance(
                    field, (relations.PrimaryKeyRelatedField, relations.HyperlinkedRelatedField)
            ):
                resolved, relation = utils.get_relation_instance(
                    resource_instance, '%s_id' % source, field.parent
                )
                if not resolved:
                    continue
                relation_id = relation if resource.get(field_name) else None
                relation_data = {
                    'data': (
                        OrderedDict([
                            ('type', relation_type), ('id', encoding.force_text(relation_id))
                        ])
                        if relation_id is not None else None)
                }

                if (
                    isinstance(field, relations.HyperlinkedRelatedField) and
                    resource.get(field_name)
                ):
                    relation_data.update(
                        {
                            'links': {
                                'related': resource.get(field_name)
                            }
                        }
                    )
                data.update({field_name: relation_data})
                continue

            if isinstance(field, relations.ManyRelatedField):
                resolved, relation_instance = utils.get_relation_instance(
                    resource_instance, source, field.parent
                )
                if not resolved:
                    continue

                relation_data = {}

                if isinstance(resource.get(field_name), collections_abc.Iterable):
                    relation_data.update(
                        {
                            'meta': {'count': len(resource.get(field_name))}
                        }
                    )

                if isinstance(field.child_relation, ResourceRelatedField):
                    # special case for ResourceRelatedField
                    relation_data.update(
                        {'data': resource.get(field_name)}
                    )

                if isinstance(field.child_relation, HyperlinkedMixin):
                    field_links = field.child_relation.get_links(
                        resource_instance,
                        field.child_relation.related_link_lookup_field
                    )
                    relation_data.update(
                        {'links': field_links}
                        if field_links else dict()
                    )

                    data.update({field_name: relation_data})
                    continue

                relation_data = list()
                for nested_resource_instance in relation_instance:
                    nested_resource_instance_type = (
                        relation_type or
                        utils.get_resource_type_from_instance(nested_resource_instance)
                    )

                    relation_data.append(OrderedDict([
                        ('type', nested_resource_instance_type),
                        ('id', encoding.force_text(nested_resource_instance.pk))
                    ]))
                data.update({
                    field_name: {
                        'data': relation_data,
                        'meta': {
                            'count': len(relation_data)
                        }
                    }
                })
                continue

            if isinstance(field, ListSerializer):
                resolved, relation_instance = utils.get_relation_instance(
                    resource_instance, source, field.parent
                )
                if not resolved:
                    continue

                relation_data = list()

                serializer_data = resource.get(field_name)
                resource_instance_queryset = list(relation_instance)
                if isinstance(serializer_data, list):
                    for position in range(len(serializer_data)):
                        nested_resource_instance = resource_instance_queryset[position]
                        nested_resource_instance_type = (
                            relation_type or
                            utils.get_resource_type_from_instance(nested_resource_instance)
                        )

                        relation_data.append(OrderedDict([
                            ('type', nested_resource_instance_type),
                            ('id', encoding.force_text(nested_resource_instance.pk))
                        ]))

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

            if isinstance(field, Serializer):
                relation_instance_id = getattr(resource_instance, source + "_id", None)
                if not relation_instance_id:
                    resolved, relation_instance = utils.get_relation_instance(
                        resource_instance, source, field.parent
                    )
                    if not resolved:
                        continue

                    if relation_instance is not None:
                        relation_instance_id = relation_instance.pk

                data.update({
                    field_name: {
                        'data': (
                            OrderedDict([
                                ('type', relation_type),
                                ('id', encoding.force_text(relation_instance_id))
                            ]) if resource.get(field_name) else None)
                    }
                })
                continue

        return utils._format_object(data)
Exemple #15
0
    def get_field_info(self, field):
        """
        Given an instance of a serializer field, return a dictionary
        of metadata about it.
        """
        field_info = OrderedDict()
        serializer = field.parent

        if isinstance(field, serializers.ManyRelatedField):
            field_info["type"] = self.type_lookup[field.child_relation]
        else:
            field_info["type"] = self.type_lookup[field]

        try:
            serializer_model = getattr(serializer.Meta, "model")
            field_info["relationship_type"] = self.relation_type_lookup[
                getattr(serializer_model, field.field_name)
            ]
        except KeyError:
            pass
        except AttributeError:
            pass
        else:
            field_info["relationship_resource"] = get_related_resource_type(field)

        field_info["required"] = getattr(field, "required", False)

        attrs = [
            "read_only",
            "write_only",
            "label",
            "help_text",
            "min_length",
            "max_length",
            "min_value",
            "max_value",
            "initial",
        ]

        for attr in attrs:
            value = getattr(field, attr, None)
            if value is not None and value != "":
                field_info[attr] = force_str(value, strings_only=True)

        if getattr(field, "child", None):
            field_info["child"] = self.get_field_info(field.child)
        elif getattr(field, "fields", None):
            field_info["children"] = self.get_serializer_info(field)

        if (
            not field_info.get("read_only")
            and not field_info.get("relationship_resource")
            and hasattr(field, "choices")
        ):
            field_info["choices"] = [
                {
                    "value": choice_value,
                    "display_name": force_str(choice_name, strings_only=True),
                }
                for choice_value, choice_name in field.choices.items()
            ]

        if (
            hasattr(serializer, "included_serializers")
            and "relationship_resource" in field_info
        ):
            field_info["allows_include"] = (
                field.field_name in serializer.included_serializers
            )

        return field_info