Required vs Default and reproducing the request in 0.4


#1

The apistar app I have is basically a proxy to another api, and while converting it to 0.4.3, I can across an interesting side effect of the way required fields are defined now.

Previously, if I created an instance of a type that had optional fields, when that instance was passed to a request as the json parameter, it would be faithfully reproduced, with defaults of optional types not withstanding. i.e.

class NbnTestingSingleEndLineTestRequest(typesystem.Object):
    properties = {
        'packetSize': typesystem.integer(minimum=64, maximum=1500, default=1400),
        'other': typesystem.string()
    }

data = NbnTestingSingleEndLineTestRequest()
assert {'packetSize': 1400} == data

Because the type has no required attribute all fields were optional, and would only be populated if there was a default.

Now, the method of defining an optional field is to provide a default

class NbnTestingSingleEndLineTestRequest(types.Type):
    packetSize = validators.Integer(minimum=64, maximum=1500, default=1400)
    other = validators.String(default=None)

but under the same test, both optional fields are included in the output, and you need to convert to a dict() to serialize to json.

data = NbnTestingSingleEndLineTestRequest({})
assert {'packetSize': 1400, 'other': None} == dict(data)

It would be great to have something to distinguish fields that are optional and do not have defaults, and are not included when serialized to json, which would result in something close to what 0.3.9 had.

class NbnTestingSingleEndLineTestRequest(types.Type):
    packetSize = validators.Integer(minimum=64, maximum=1500, default=1400)
    other = validators.String()

data = NbnTestingSingleEndLineTestRequest()
assert {'packetSize': 1400} == data

Or, perhaps when serializing to a dict, if the field is None and allow_none is False, then don’t include the field, because None would not be a valid value for the String field anyway.

I’m not sure if null/None values for optional fields that would otherwise not have been included in the request are a problem for our upstream, but it sure looks messy having to include every field in the expected output of a unit test. I may write a custom serializer that will not include None items where the default is None and allow_null is False.


#2

For now, I’m using a mixin to strip optional fields that have not had values provided.

class StripOptionalMixin(object):
    """Strip optional fields when serializing to dict

    The issue in 0.4.3 is that optional fields are determined by specifying a default.
    If the default is None, and allow_null is False, then one might assume the value was never provided
    and should not be output when serializing the instance.
    """
    def __iter__(self):
        for key in self._dict:
            value = self._dict[key]
            validator = self.validator.properties[key]
            if value is not None:
                yield key
            elif validator.default is None and validator.allow_null:
                yield key

Seems to work, but I’m not sure if the side effects of filtering at this level.