DRF - Automatic Deep Serialization of OneToOne Field

I came across some behavior that doesn’t quite look right to me, and could possibly be a bug in the way Django REST treats automatic deep serialization.

The depth argument of the ModelSerializer.Meta class is used to expand relations of the serialized models automatically.

It creates a NestedSerializer on the fly:

def build_nested_field(self, field_name, relation_info, nested_depth):
        """
        Create nested fields for forward and reverse relationships.
        """
        class NestedSerializer(ModelSerializer):
            class Meta:
                model = relation_info.related_model
                depth = nested_depth - 1
                fields = '__all__'

        field_class = NestedSerializer
        field_kwargs = get_nested_relation_kwargs(relation_info)

        return field_class, field_kwargs

This method is called on each relation field of the model, if the serializer has a depth parameter >0.

def build_field(self, field_name, info, model_class, nested_depth):
        """
        Return a two tuple of (cls, kwargs) to build a serializer field with.
        """
        if field_name in info.fields_and_pk:
            model_field = info.fields_and_pk[field_name]
            return self.build_standard_field(field_name, model_field)

        elif field_name in info.relations:
            relation_info = info.relations[field_name]
            if not nested_depth:
                return self.build_relational_field(field_name, relation_info)
            else:
                return self.build_nested_field(field_name, relation_info, nested_depth)

        # ...

The info object is obtained by calling this method:

def get_field_info(model):
    """
    Given a model class, returns a `FieldInfo` instance, which is a
    `namedtuple`, containing metadata about the various field types on the model
    including information about their relationships.
    """
    opts = model._meta.concrete_model._meta
    

    pk = _get_pk(opts)
    fields = _get_fields(opts)
    forward_relations = _get_forward_relationships(opts)
    reverse_relations = _get_reverse_relationships(opts)
    fields_and_pk = _merge_fields_and_pk(pk, fields)
    relationships = _merge_relationships(forward_relations, reverse_relations)

    return FieldInfo(pk, fields, forward_relations, reverse_relations,
                     fields_and_pk, relationships)

and the _get_forward_relationships method looks like this:

def _get_forward_relationships(opts):
    """
    Returns an `OrderedDict` of field names to `RelationInfo`.
    """
    forward_relations = OrderedDict()
    for field in [field for field in opts.fields if field.serialize and field.remote_field]:
        forward_relations[field.name] = RelationInfo(
            model_field=field,
            related_model=field.remote_field.model,
            to_many=False,
            to_field=_get_to_field(field),
            has_through_model=False,
            reverse=False
        )
    # ...

Apparently, the issue is that a OneToOne field has its serialize parameter set to False. As a result, it’s not included in the relations dict and isn’t expanded into a NestedSerializer, but just shown as a pk of the related object.

Is this the expected behavior? Shouldn’t a OneToOne field behave like a ForeignKey, where the related object is serialized using a NestedSerializer?

Note: This isn’t an official support channel for the Django Rest Framework.

While there are people here willing to try to help with problems using it, this isn’t the best place to look for authoritative answers regarding design decisions. Any responses you get here are likely going to be conjectures made and conclusions obtained by other users of the framework.

See Support for the best places to try to get official or authoritative responses.

Got you, I posted this on the official mailing list as well. Thanks