Skip to content

Rendering of Nested Serializer fails if no dictionary is parsed #7706

@s4ke

Description

@s4ke

Checklist

  • I have verified that that issue exists against the master branch of Django REST framework.
  • I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
  • This is not a usage question. (Those should be directed to the discussion group instead.)
  • This cannot be dealt with as a third party library. (We prefer new functionality to be in the form of third party libraries where possible.)
  • I have reduced the issue to the simplest possible case.
  • I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)

Steps to reproduce

  1. Create a Serializer with a nested Serializer, e.g.:
class MySerializer(Serializer):
   payload = PayloadSerializer(default={}, write_only=True)

class PayloadSerializer(Serializer):
  some_field = CharField()
  1. Post a malformed payload that is not a dictionary:
{
   "payload": 1
}

Expected behavior

Nested Serializer should fail and throw a ValidationError, Browsable API should render empty fields.

Actual behavior

Server crashes in serializer_helpers.py at in as_form_field because integer is not iterable:

    def as_form_field(self):
        values = {}
        for key, value in self.value.items():
            if isinstance(value, (list, dict)):
                values[key] = value
            else:
                values[key] = '' if (value is None or value is False) else force_str(value)
        return self.__class__(self._field, values, self.errors, self._prefix)

My workaround, modify Parent serializer:

class PayloadNestedField(NestedBoundField):
    def __init__(self, field, value, errors, prefix=''):
        if value is not None and not isinstance(value, dict):
            value = {}
        super().__init__(field, value, errors, prefix)

class PayloadSerializer(Serializer):
   some_field = CharField()

   def __getitem__(self, key):
            field = self.fields[key]
            value = self.data.get(key)
            error = self.errors.get(key) if hasattr(self, '_errors') else None
            if key == 'payload':
                return PayloadNestedField(field, value, error)
            else:
                return super().__getitem__(key)

I think the issue here is that NestedBoundField only defaults the value to {} if it is None or the empty string here:

class NestedBoundField(BoundField):
    """
    This `BoundField` additionally implements __iter__ and __getitem__
    in order to support nested bound fields. This class is the type of
    `BoundField` that is used for serializer fields.
    """

    def __init__(self, field, value, errors, prefix=''):
        if value is None or value == '':
            value = {}
        super().__init__(field, value, errors, prefix)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions