示例#1
0
def test_get_included_serializers():
    class IncludedSerializersModel(DJAModel):
        self = models.ForeignKey("self", on_delete=models.CASCADE)
        target = models.ForeignKey(ManyToManyTarget, on_delete=models.CASCADE)
        other_target = models.ForeignKey(ManyToManyTarget, on_delete=models.CASCADE)

        class Meta:
            app_label = "tests"

    class IncludedSerializersSerializer(serializers.ModelSerializer):
        included_serializers = {
            "self": "self",
            "target": ManyToManyTargetSerializer,
            "other_target": "tests.serializers.ManyToManyTargetSerializer",
        }

        class Meta:
            model = IncludedSerializersModel
            fields = ("self", "other_target", "target")

    included_serializers = get_included_serializers(IncludedSerializersSerializer)
    expected_included_serializers = {
        "self": IncludedSerializersSerializer,
        "target": ManyToManyTargetSerializer,
        "other_target": ManyToManyTargetSerializer,
    }

    assert included_serializers == expected_included_serializers
 def test_included_serializers(self):
     serializers = get_included_serializers(DummySerializer)
     self.assertIn("related", serializers)
     self.assertEqual(
         serializers["related"](DummyRelated(pk=42, name="Test")).data,
         {
             "id": 42,
             "name": "Test"
         },
     )
示例#3
0
def test_get_included_serializers_against_class():
    klass = SerializerWithIncludedSerializers
    included_serializers = get_included_serializers(klass)
    expected_included_serializers = {
        'blog': BlogSerializer,
        'authors': AuthorSerializer,
        'comments': CommentSerializer,
        'self': klass
    }
    assert (six.viewkeys(included_serializers) == six.viewkeys(
        klass.included_serializers), 'the keys must be preserved')

    assert included_serializers == expected_included_serializers
def test_get_included_serializers_against_class():
    klass = SerializerWithIncludedSerializers
    included_serializers = get_included_serializers(klass)
    expected_included_serializers = {
        'blog': BlogSerializer,
        'authors': AuthorSerializer,
        'comments': CommentSerializer,
        'self': klass
    }
    assert (six.viewkeys(included_serializers) == six.viewkeys(klass.included_serializers),
            'the keys must be preserved')

    assert included_serializers == expected_included_serializers
def test_get_included_serializers_against_instance():
    klass = SerializerWithIncludedSerializers
    instance = klass()
    included_serializers = get_included_serializers(instance)
    expected_included_serializers = {
        'blog': BlogSerializer,
        'authors': AuthorSerializer,
        'comments': CommentSerializer,
        'self': klass
    }
    assert included_serializers.keys() == klass.included_serializers.keys(), (
        'the keys must be preserved')

    assert included_serializers == expected_included_serializers
def test_substance_fetch_includes(client, admin_user, substance_factory):
    client.force_authenticate(user=admin_user)
    model = substance_factory(illdefined=True).instance
    requested_includes = get_included_serializers(SubstanceSerializer)
    # Pop common info related fields.
    for field in ["created_by", "updated_by"]:
        requested_includes.pop(field)
    resp = client.get(
        "/substances/{}".format(model.pk),
        data={"include": ",".join(requested_includes)},
    )
    assert resp.status_code == 200
    response_included = json.loads(resp.content.decode("utf-8"))["included"]
    # Assert there is an include resource for every requested include
    assert len(requested_includes) == len(response_included)
 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 = 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)
示例#8
0
 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)
    def to_representation(self, value):
        if getattr(self, 'pk_field', None) is not None:
            pk = self.pk_field.to_representation(value.pk)
        else:
            pk = value.pk

        # check to see if this resource has a different resource_name when
        # included and use that name
        resource_type = None
        root = getattr(self.parent, 'parent', self.parent)
        field_name = self.field_name if self.field_name else self.parent.field_name
        if getattr(root, 'included_serializers', None) is not None:
            includes = get_included_serializers(root)
            if field_name in includes.keys():
                resource_type = get_resource_type_from_serializer(includes[field_name])

        resource_type = resource_type if resource_type else get_resource_type_from_instance(value)
        return OrderedDict([('type', resource_type), ('id', str(pk))])
示例#10
0
    def to_representation(self, value):
        # force pk to be UUID
        pk = getattr(value, 'uuid', getattr(value, 'pk'))

        # check to see if this resource has a different resource_name when
        # included and use that name
        resource_type = None
        root = getattr(self.parent, 'parent', self.parent)
        field_name = self.field_name if self.field_name else self.parent.field_name
        if getattr(root, 'included_serializers', None) is not None:
            includes = get_included_serializers(root)
            if field_name in includes.keys():
                resource_type = get_resource_type_from_serializer(
                    includes[field_name])

        resource_type = resource_type if resource_type else get_resource_type_from_instance(
            value)
        return OrderedDict([('type', resource_type.lower()), ('id', str(pk))])
示例#11
0
    def to_representation(self, value):
        if getattr(self, 'pk_field', None) is not None:
            pk = self.pk_field.to_representation(value.pk)
        else:
            pk = value.pk

        # check to see if this resource has a different resource_name when
        # included and use that name
        resource_type = None
        root = getattr(self.parent, 'parent', self.parent)
        field_name = self.field_name if self.field_name else self.parent.field_name
        if getattr(root, 'included_serializers', None) is not None:
            includes = get_included_serializers(root)
            if field_name in includes.keys():
                resource_type = get_resource_type_from_serializer(
                    includes[field_name])

        resource_type = resource_type if resource_type else get_resource_type_from_instance(
            value)
        return OrderedDict([('type', resource_type), ('auction', str(pk))])
示例#12
0
def fixture(
    deterministic_uuids,
    django_db_reset_sequences,
    request,
    viewset,
):
    """Get fixture and many to many relations of given viewset."""
    fixture = request.getfixturevalue(viewset.factory_name)

    included = get_included_serializers(viewset.serializer_class)
    for name in sorted(included.keys()):
        relation_type = getattr(fixture.__class__, name)
        # pytest factory boy doesn't have native ManyToMany support
        # so needs to be handled manually
        if isinstance(relation_type, ManyToManyDescriptor):
            print("{0}_{1}".format(viewset.factory_name, name))
            request.getfixturevalue("{0}_{1}".format(viewset.factory_name,
                                                     name))

    return fixture
示例#13
0
    def get_resource_type_from_included_serializer(self):
        """
        Check to see it this resource has a different resource_name when
        included and return that name, or None
        """
        field_name = self.field_name or self.parent.field_name
        parent = self.get_parent_serializer()

        if parent is not None:
            # accept both singular and plural versions of field_name
            field_names = [
                inflection.singularize(field_name),
                inflection.pluralize(field_name)
            ]
            includes = get_included_serializers(parent)
            for field in field_names:
                if field in includes.keys():
                    return get_resource_type_from_serializer(includes[field])

        return None
    def _get_included_serializers(cls,
                                  serializer,
                                  prefix="",
                                  already_seen=None):
        if not already_seen:
            already_seen = set()

        if serializer in already_seen:
            return []

        included_serializers = []
        already_seen.add(serializer)

        for include, included_serializer in utils.get_included_serializers(
                serializer).items():
            included_serializers.append(f"{prefix}{include}")
            included_serializers.extend(
                cls._get_included_serializers(
                    included_serializer,
                    f"{prefix}{include}.",
                    already_seen=already_seen,
                ))

        return included_serializers
def test_synonym_serializer_includes():
    serializer = get_included_serializers(SynonymSerializer)
    assert serializer["source"] is SourceSerializer
    assert serializer["substance"] is SubstanceSerializer
    assert serializer["synonym_quality"] is SynonymQualitySerializer
    assert serializer["synonym_type"] is SynonymTypeSerializer
def test_substance_serializer_includes():
    serializer = get_included_serializers(SubstanceSerializer)
    assert serializer["source"] is SourceSerializer
    assert serializer["substance_type"] is SubstanceTypeSerializer
    assert serializer["qc_level"] is QCLevelsTypeSerializer
    assert serializer["associated_compound"] is CompoundSerializer
    def extract_included(cls, fields, resource, resource_instance, included_resources,
                         included_cache):
        """
        Adds related data to the top level included key when the request includes
        ?include=example,example_field2
        """
        # this function may be called with an empty record (example: Browsable Interface)
        if not resource_instance:
            return

        current_serializer = fields.serializer
        context = current_serializer.context
        included_serializers = utils.get_included_serializers(current_serializer)
        included_resources = copy.copy(included_resources)
        included_resources = [inflection.underscore(value) for value in included_resources]

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

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

            try:
                included_resources.remove(field_name)
            except ValueError:
                # Skip fields not in requested included resources
                # If no child field, directly continue with the next field
                if field_name not in [node.split('.')[0] for node in included_resources]:
                    continue

            relation_instance = cls.extract_relation_instance(
                field_name, field, resource_instance, current_serializer
            )
            if isinstance(relation_instance, Manager):
                relation_instance = relation_instance.all()

            serializer_data = resource.get(field_name)

            if isinstance(field, relations.ManyRelatedField):
                serializer_class = included_serializers[field_name]
                field = serializer_class(relation_instance, many=True, context=context)
                serializer_data = field.data

            if isinstance(field, relations.RelatedField):
                if relation_instance is None or not serializer_data:
                    continue

                many = field._kwargs.get('child_relation', None) is not None

                if isinstance(field, ResourceRelatedField) and not many:
                    already_included = serializer_data['type'] in included_cache and \
                        serializer_data['id'] in included_cache[serializer_data['type']]

                    if already_included:
                        continue

                serializer_class = included_serializers[field_name]
                field = serializer_class(relation_instance, many=many, context=context)
                serializer_data = field.data

            new_included_resources = [key.replace('%s.' % field_name, '', 1)
                                      for key in included_resources
                                      if field_name == key.split('.')[0]]

            if isinstance(field, ListSerializer):
                serializer = field.child
                relation_type = utils.get_resource_type_from_serializer(serializer)
                relation_queryset = list(relation_instance)

                if serializer_data:
                    for position in range(len(serializer_data)):
                        serializer_resource = serializer_data[position]
                        nested_resource_instance = relation_queryset[position]
                        resource_type = (
                            relation_type or
                            utils.get_resource_type_from_instance(nested_resource_instance)
                        )
                        serializer_fields = utils.get_serializer_fields(
                            serializer.__class__(
                                nested_resource_instance, context=serializer.context
                            )
                        )
                        new_item = cls.build_json_resource_obj(
                            serializer_fields,
                            serializer_resource,
                            nested_resource_instance,
                            resource_type,
                            getattr(serializer, '_poly_force_type_resolution', False)
                        )
                        included_cache[new_item['type']][new_item['id']] = \
                            utils._format_object(new_item)
                        cls.extract_included(
                            serializer_fields,
                            serializer_resource,
                            nested_resource_instance,
                            new_included_resources,
                            included_cache,
                        )

            if isinstance(field, Serializer):
                relation_type = utils.get_resource_type_from_serializer(field)

                # Get the serializer fields
                serializer_fields = utils.get_serializer_fields(field)
                if serializer_data:
                    new_item = cls.build_json_resource_obj(
                        serializer_fields,
                        serializer_data,
                        relation_instance,
                        relation_type,
                        getattr(field, '_poly_force_type_resolution', False)
                    )
                    included_cache[new_item['type']][new_item['id']] = utils._format_object(
                        new_item
                    )
                    cls.extract_included(
                        serializer_fields,
                        serializer_data,
                        relation_instance,
                        new_included_resources,
                        included_cache,
                    )
示例#18
0
    def extract_included(cls, fields, resource, resource_instance,
                         included_resources, included_cache):
        """
        Adds related data to the top level included key when the request includes
        ?include=example,example_field2
        """
        # this function may be called with an empty record (example: Browsable Interface)
        if not resource_instance:
            return

        current_serializer = fields.serializer
        context = current_serializer.context
        included_serializers = utils.get_included_serializers(
            current_serializer)
        included_resources = copy.copy(included_resources)
        included_resources = [
            inflection.underscore(value) for value in included_resources
        ]
        render_nested_as_attribute = json_api_settings.SERIALIZE_NESTED_SERIALIZERS_AS_ATTRIBUTE

        for field_name, field in iter(fields.items()):
            # 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

            if isinstance(field,
                          BaseSerializer) and render_nested_as_attribute:
                continue

            try:
                included_resources.remove(field_name)
            except ValueError:
                # Skip fields not in requested included resources
                # If no child field, directly continue with the next field
                if field_name not in [
                        node.split('.')[0] for node in included_resources
                ]:
                    continue

            relation_instance = cls.extract_relation_instance(
                field, resource_instance)
            if isinstance(relation_instance, Manager):
                relation_instance = relation_instance.all()

            serializer_data = resource.get(field_name)

            if isinstance(field, relations.ManyRelatedField):
                serializer_class = included_serializers[field_name]
                field = serializer_class(relation_instance,
                                         many=True,
                                         context=context)
                serializer_data = field.data

            if isinstance(field, relations.RelatedField):
                if relation_instance is None or not serializer_data:
                    continue

                many = field._kwargs.get('child_relation', None) is not None

                if isinstance(field, ResourceRelatedField) and not many:
                    already_included = serializer_data['type'] in included_cache and \
                        serializer_data['id'] in included_cache[serializer_data['type']]

                    if already_included:
                        continue

                serializer_class = included_serializers[field_name]
                field = serializer_class(relation_instance,
                                         many=many,
                                         context=context)
                serializer_data = field.data

            new_included_resources = [
                key.replace('%s.' % field_name, '', 1)
                for key in included_resources
                if field_name == key.split('.')[0]
            ]

            if isinstance(field, ListSerializer):
                serializer = field.child
                relation_type = utils.get_resource_type_from_serializer(
                    serializer)
                relation_queryset = list(relation_instance)

                if serializer_data:
                    for position in range(len(serializer_data)):
                        serializer_resource = serializer_data[position]
                        nested_resource_instance = relation_queryset[position]
                        resource_type = (relation_type or
                                         utils.get_resource_type_from_instance(
                                             nested_resource_instance))
                        serializer_fields = utils.get_serializer_fields(
                            serializer.__class__(nested_resource_instance,
                                                 context=serializer.context))
                        new_item = cls.build_json_resource_obj(
                            serializer_fields, serializer_resource,
                            nested_resource_instance, resource_type,
                            getattr(serializer, '_poly_force_type_resolution',
                                    False))
                        included_cache[new_item['type']][new_item['id']] = \
                            utils.format_field_names(new_item)
                        cls.extract_included(
                            serializer_fields,
                            serializer_resource,
                            nested_resource_instance,
                            new_included_resources,
                            included_cache,
                        )

            if isinstance(field, Serializer):
                relation_type = utils.get_resource_type_from_serializer(field)

                # Get the serializer fields
                serializer_fields = utils.get_serializer_fields(field)
                if serializer_data:
                    new_item = cls.build_json_resource_obj(
                        serializer_fields, serializer_data, relation_instance,
                        relation_type,
                        getattr(field, '_poly_force_type_resolution', False))
                    included_cache[new_item['type']][
                        new_item['id']] = utils.format_field_names(new_item)
                    cls.extract_included(
                        serializer_fields,
                        serializer_data,
                        relation_instance,
                        new_included_resources,
                        included_cache,
                    )
示例#19
0
    def extract_included(cls, request, fields, resource, resource_instance,
                         included_resources):
        # this function may be called with an empty record (example: Browsable Interface)
        if not resource_instance:
            return

        included_data = list()
        current_serializer = fields.serializer
        context = current_serializer.context
        included_serializers = utils.get_included_serializers(
            current_serializer)
        included_resources = copy.copy(included_resources)
        included_resources = [
            inflection.underscore(value) for value in included_resources
        ]

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

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

            try:
                included_resources.remove(field_name)
            except ValueError:
                # Skip fields not in requested included resources
                # If no child field, directly continue with the next field
                if field_name not in [
                        node.split('.')[0] for node in included_resources
                ]:
                    continue

            try:
                relation_instance = getattr(resource_instance, field_name)
            except AttributeError:
                try:
                    # For ManyRelatedFields if `related_name` is not set we need to access `foo_set` from `source`
                    relation_instance = getattr(resource_instance,
                                                field.child_relation.source)
                except AttributeError:
                    if not hasattr(current_serializer, field.source):
                        continue
                    serializer_method = getattr(current_serializer,
                                                field.source)
                    relation_instance = serializer_method(resource_instance)

            if isinstance(relation_instance, Manager):
                relation_instance = relation_instance.all()

            new_included_resources = [
                key.replace('%s.' % field_name, '', 1)
                for key in included_resources
                if field_name == key.split('.')[0]
            ]
            serializer_data = resource.get(field_name)

            if isinstance(field, RemoteResourceField):
                user_id = getattr(request.user, 'id', None)
                roles = request.user.roles
                pk = serializer_data.get('id')

                include = ",".join(new_included_resources)
                try:
                    remote_resource = event_client.get_remote_resource_data(
                        field_name,
                        pk=pk,
                        user_id=user_id,
                        include=include,
                        page_size=1000,
                        roles=roles)

                    body = ujson.loads(remote_resource['body'])

                    if 400 <= remote_resource['status'] < 600:
                        raise RemoteResourceIncludeError(
                            field_name, body["errors"][0])
                except RequestTimeout:
                    raise RemoteResourceIncludeTimeoutError(field_name)

                included_data.append(body['data'])

                if body.get('included'):
                    included_data.extend(body['included'])

                # We continue here since RemoteResourceField inherits
                # form ResourceRelatedField which is a RelatedField
                continue

            if isinstance(field, relations.ManyRelatedField):
                serializer_class = included_serializers[field_name]
                field = serializer_class(relation_instance,
                                         many=True,
                                         context=context)
                serializer_data = field.data

            if isinstance(field, relations.RelatedField):
                if relation_instance is None:
                    continue

                many = field._kwargs.get('child_relation', None) is not None
                serializer_class = included_serializers[field_name]
                field = serializer_class(relation_instance,
                                         many=many,
                                         context=context)
                serializer_data = field.data

            if isinstance(field, ListSerializer):
                serializer = field.child
                relation_type = utils.get_resource_type_from_serializer(
                    serializer)
                relation_queryset = list(relation_instance)

                # Get the serializer fields
                serializer_fields = utils.get_serializer_fields(serializer)
                if serializer_data:
                    for position in range(len(serializer_data)):
                        serializer_resource = serializer_data[position]
                        nested_resource_instance = relation_queryset[position]
                        resource_type = (relation_type or
                                         utils.get_resource_type_from_instance(
                                             nested_resource_instance))
                        included_data.append(
                            cls.build_json_resource_obj(
                                serializer_fields, serializer_resource,
                                nested_resource_instance, resource_type))
                        included_data.extend(
                            cls.extract_included(request, serializer_fields,
                                                 serializer_resource,
                                                 nested_resource_instance,
                                                 new_included_resources))

            if isinstance(field, Serializer):

                relation_type = utils.get_resource_type_from_serializer(field)

                # Get the serializer fields
                serializer_fields = utils.get_serializer_fields(field)
                if serializer_data:
                    included_data.append(
                        cls.build_json_resource_obj(serializer_fields,
                                                    serializer_data,
                                                    relation_instance,
                                                    relation_type))
                    included_data.extend(
                        cls.extract_included(request, serializer_fields,
                                             serializer_data,
                                             relation_instance,
                                             new_included_resources))

        return key_formatter()(included_data)
示例#20
0
    def extract_included(cls, fields, resource, resource_instance,
                         included_resources):
        # this function may be called with an empty record (example: Browsable Interface)
        if not resource_instance:
            return

        included_data = list()
        current_serializer = fields.serializer
        context = current_serializer.context
        included_serializers = utils.get_included_serializers(
            current_serializer)
        included_resources = copy.copy(included_resources)
        included_resources = [
            inflection.underscore(value) for value in included_resources
        ]

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

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

            try:
                included_resources.remove(field_name)
            except ValueError:
                # Skip fields not in requested included resources
                # If no child field, directly continue with the next field
                if field_name not in [
                        node.split('.')[0] for node in included_resources
                ]:
                    continue

            try:
                relation_instance = getattr(resource_instance, field_name)
            except AttributeError:
                try:
                    # For ManyRelatedFields if `related_name` is not set we need to access `foo_set` from `source`
                    relation_instance = getattr(resource_instance,
                                                field.child_relation.source)
                except AttributeError:
                    if not hasattr(current_serializer, field.source):
                        continue
                    serializer_method = getattr(current_serializer,
                                                field.source)
                    relation_instance = serializer_method(resource_instance)

            if isinstance(relation_instance, Manager):
                relation_instance = relation_instance.all()

            new_included_resources = [
                key.replace('%s.' % field_name, '', 1)
                for key in included_resources
                if field_name == key.split('.')[0]
            ]
            serializer_data = resource.get(field_name)

            if isinstance(field, relations.ManyRelatedField):
                serializer_class = included_serializers[field_name]
                field = serializer_class(relation_instance,
                                         many=True,
                                         context=context)
                serializer_data = field.data

            if isinstance(field, relations.RelatedField):
                if relation_instance is None:
                    continue

                many = field._kwargs.get('child_relation', None) is not None
                serializer_class = included_serializers[field_name]
                field = serializer_class(relation_instance,
                                         many=many,
                                         context=context)
                serializer_data = field.data

            if isinstance(field, ListSerializer):
                serializer = field.child
                relation_type = utils.get_resource_type_from_serializer(
                    serializer)
                relation_queryset = list(relation_instance)

                # Get the serializer fields
                serializer_fields = utils.get_serializer_fields(serializer)
                if serializer_data:
                    for position in range(len(serializer_data)):
                        serializer_resource = serializer_data[position]
                        nested_resource_instance = relation_queryset[position]
                        resource_type = (relation_type or
                                         utils.get_resource_type_from_instance(
                                             nested_resource_instance))
                        included_data.append(
                            cls.build_json_resource_obj(
                                serializer_fields, serializer_resource,
                                nested_resource_instance, resource_type))
                        included_data.extend(
                            cls.extract_included(serializer_fields,
                                                 serializer_resource,
                                                 nested_resource_instance,
                                                 new_included_resources))

            if isinstance(field, Serializer):

                relation_type = utils.get_resource_type_from_serializer(field)

                # Get the serializer fields
                serializer_fields = utils.get_serializer_fields(field)
                if serializer_data:
                    included_data.append(
                        cls.build_json_resource_obj(serializer_fields,
                                                    serializer_data,
                                                    relation_instance,
                                                    relation_type))
                    included_data.extend(
                        cls.extract_included(serializer_fields,
                                             serializer_data,
                                             relation_instance,
                                             new_included_resources))

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

        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:
            resource = included_serializers.get(field_name, field)
            field_info['relationship_resource'] = get_related_resource_type(
                resource)

        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, field_name)  # TODO: Is `field_name` okay here?
        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_substance_relationship_serializer_includes():
    serializer = get_included_serializers(SubstanceRelationshipSerializer)
    assert serializer["from_substance"] is SubstanceSerializer
    assert serializer["to_substance"] is SubstanceSerializer
    assert serializer["source"] is SourceSerializer
    assert serializer["relationship_type"] is RelationshipTypeSerializer