flask-marshmallow - generating Marshmallow schemas from SQLAlchemy models seemingly breaks flasgger
snctfd opened this issue · 1 comments
I have a SQLAlchemy model from which I derive a flask-marshmallow schema:
class Customer(db.Model):
__tablename__ = 'customer'
customer_id: Mapped[int] = mapped_column('pk_customer_id', primary_key=True)
first_name: Mapped[str] = mapped_column(String(100))
last_name: Mapped[str] = mapped_column(String(100))
customer_number: Mapped[int]
class CustomerSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Customer
I then add the Schema to my definitions with @swag_from
:
@customers_bp.route('/', methods=['GET'])
@swag_from({
'definitions': {
'Customer': CustomerSchema
}
})
def get_customers():
"""
Return a list of customers.
---
responses:
200:
description: A list of customers
schema:
type: object
properties:
customers:
type: array
items:
$ref: '#/definitions/Customer'
"""
...
When I head to /apidocs
, I am greeted with the following error:
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/flasgger/base.py", line 164, in get
return jsonify(self.loader())
File "/usr/local/lib/python3.9/site-packages/flask/json/__init__.py", line 170, in jsonify
return current_app.json.response(*args, **kwargs) # type: ignore[return-value]
File "/usr/local/lib/python3.9/site-packages/flask/json/provider.py", line 214, in response
f"{self.dumps(obj, **dump_args)}\n", mimetype=self.mimetype
File "/usr/local/lib/python3.9/site-packages/flask/json/provider.py", line 179, in dumps
return json.dumps(obj, **kwargs)
File "/usr/local/lib/python3.9/json/__init__.py", line 234, in dumps
return cls(
File "/usr/local/lib/python3.9/json/encoder.py", line 201, in encode
chunks = list(chunks)
File "/usr/local/lib/python3.9/json/encoder.py", line 431, in _iterencode
yield from _iterencode_dict(o, _current_indent_level)
File "/usr/local/lib/python3.9/json/encoder.py", line 405, in _iterencode_dict
yield from chunks
File "/usr/local/lib/python3.9/json/encoder.py", line 405, in _iterencode_dict
yield from chunks
File "/usr/local/lib/python3.9/json/encoder.py", line 438, in _iterencode
o = _default(o)
File "/usr/local/lib/python3.9/site-packages/flask/json/provider.py", line 121, in _default
raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
TypeError: Object of type SQLAlchemyAutoSchemaMeta is not JSON serializable
Seemingly, the Meta
object containing options for schema generation causes problems when attempting to include it in the flasgger config.
We have a similar problem with schemas created from direct imports from flask_marshmallow
. The serializer doesn't know what to do with the schema's Meta
class:
ERROR:root:jsonify failure; defaulting to json.dumps
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/flasgger/base.py", line 164, in get
return jsonify(self.loader())
File "/usr/local/lib/python3.9/site-packages/flask/json/__init__.py", line 342, in jsonify
return current_app.json.response(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/flask/json/provider.py", line 309, in response
f"{self.dumps(obj, **dump_args)}\n", mimetype=mimetype
File "/opt/swept/api/swept_api/__init__.py", line 55, in dumps
return _json.dumps(obj, cls=ExtendedJSONEncoder, **kwargs)
File "/usr/local/lib/python3.9/json/__init__.py", line 234, in dumps
return cls(
File "/usr/local/lib/python3.9/json/encoder.py", line 201, in encode
chunks = list(chunks)
File "/usr/local/lib/python3.9/json/encoder.py", line 431, in _iterencode
yield from _iterencode_dict(o, _current_indent_level)
File "/usr/local/lib/python3.9/json/encoder.py", line 405, in _iterencode_dict
yield from chunks
File "/usr/local/lib/python3.9/json/encoder.py", line 405, in _iterencode_dict
yield from chunks
File "/usr/local/lib/python3.9/json/encoder.py", line 438, in _iterencode
o = _default(o)
File "/opt/swept/api/swept_api/__init__.py", line 50, in default
return super().default(o)
File "/usr/local/lib/python3.9/json/encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type SchemaMeta is not JSON serializable
Pipfile:
apispec = "*"
flask = "==2.2.0"
flask-marshmallow = "==0.15.0"
flask-sqlalchemy = "==3.0.2"
marshmallow = "==3.9.0"
marshmallow-sqlalchemy = "==0.24.0"
sqlalchemy = "==2.0.0"
As mentioned in #590, downgrading to flasgger = "==0.9.5"
resolves the SchemaMeta
issue, but instead raises a different error for us:
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1758, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1734, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
File "/usr/local/lib/python3.9/site-packages/flask/views.py", line 107, in view
return current_app.ensure_sync(self.dispatch_request)(**kwargs)
File "/usr/local/lib/python3.9/site-packages/flask/views.py", line 188, in dispatch_request
return current_app.ensure_sync(meth)(**kwargs)
File "/usr/local/lib/python3.9/site-packages/flasgger/base.py", line 133, in get
return jsonify(self.loader())
File "/usr/local/lib/python3.9/site-packages/flasgger/base.py", line 399, in get_apispecs
specs = get_specs(
File "/usr/local/lib/python3.9/site-packages/flasgger/utils.py", line 134, in get_specs
convert_schemas(apispec_swag, apispec_definitions)
File "/usr/local/lib/python3.9/site-packages/flasgger/marshmallow_apispec.py", line 109, in convert_schemas
v = convert_schemas(v, definitions)
File "/usr/local/lib/python3.9/site-packages/flasgger/marshmallow_apispec.py", line 123, in convert_schemas
definitions[v.__name__] = schema2jsonschema(v)
File "/usr/local/lib/python3.9/site-packages/apispec/ext/marshmallow/openapi.py", line 256, in schema2jsonschema
jsonschema = self.fields2jsonschema(fields, partial=partial)
File "/usr/local/lib/python3.9/site-packages/apispec/ext/marshmallow/openapi.py", line 281, in fields2jsonschema
prop = self.field2property(field_obj)
File "/usr/local/lib/python3.9/site-packages/apispec/ext/marshmallow/field_converter.py", line 174, in field2property
ret.update(attr_func(field, ret=ret))
File "/usr/local/lib/python3.9/site-packages/apispec/ext/marshmallow/field_converter.py", line 224, in field2default
default = field.load_default
AttributeError: 'String' object has no attribute 'load_default'
The only schema currently attached to a view is extremely simple:
from flasgger import Schema, fields
class InviteUserSchema(Schema):
class Meta:
strict = True
message = fields.Str(required=True)
subject = fields.Str(required=True)
In the view extended from SwaggerView
, we pass the schema to definitions
. If this line is commented out, the docs load without the schema definition, otherwise they raise the error above:
definitions = {"InviteUserSchema": InviteUserSchema}