pythonflaskflask-restplus

Flask-Restplus: how to model string or object?


In Flask-Restplus, I need to model an attribute value that maybe either a list of strings or a list of objects.

That is it can look like this:

{
    'my_attribute': [
         'value1',
          'value2'
     ]
}

or it can look like the following:

{
    'my_attribute': [
        {
             'name': 'value1',
              'foo': 'something'
         },
         {
              'name': 'value2',
               'foo': 'something else'
          }
     ]
}

How should I model that in Flask-Restplus’ api.model?


Solution

  • I've just figured this out myself. In short, create a custom field class that emits its own JSON schema. In turn the schema uses the oneOf type to specify that this is either a string or an object.

    from flask_restplus import fields
    
    element_object = api.model('Element_Object', {
        'name': fields.String(),
        'foo': fields.String()
    })
    
    class StringOrObjectElement(fields.Nested):
        __schema_type__ = ['string','object']
    
        def output(self, key, obj):
            if isinstance(obj, str):
                if key == 'name':
                    return obj
                else:
                    return 'default_value'
            return super().output(key, obj)
    
        def schema(self):
            schema_dict = super().schema()
            schema_dict.pop('type')
            nested_ref = schema_dict.pop('$ref')
            schema_dict['oneOf'] = [
                {
                    'type': 'string'
                },
                {
                    '$ref': nested_ref
                }
            ]
            return schema_dict
    
    root_object = api.model('Root_Object', {
        'my_attribute': fields.List(fields.StringOrObjectElement(element_object))