apryor6 / flask_accepts Goto Github PK
View Code? Open in Web Editor NEWEasy, opinionated Flask input/output handling mixing Marshmallow with flask-restx
License: BSD 3-Clause "New" or "Revised" License
Easy, opinionated Flask input/output handling mixing Marshmallow with flask-restx
License: BSD 3-Clause "New" or "Revised" License
Flask-RESTPlus has the ability to set field descriptions that appear in swagger like so:
https://flask-restplus.readthedocs.io/en/stable/swagger.html#documenting-the-fields
Is something like this possible in flask_accepts with marshmallow? I can't find anything in the documentation and a quick glance over the flask_accepts source indicates that any "metadata" set on a marshmallow field is ignored during the mapping process. I imagine that something like this would be ideal:
from marshmallow import Schema, fields
class SomeSchema(Schema):
some_field = fields.String(metadata={"description":"Some Field"})
Reference for marshmallow field metadata: https://marshmallow.readthedocs.io/en/stable/api_reference.html#module-marshmallow.fields
Any ideas?
Hi, great library.
There's a typo in dev-requirement.txt
, flask-restx version is flask-restx==0.12.1
, which doesn't exist.
Hi,
I wanted to use custom marshmallow field related to mongo data serialisation
class ObjectIdField(fields.Field):
def _deserialize(self, value, attr, data, **kwargs):
try:
return bson.ObjectId(value)
except Exception:
raise ValidationError('invalid ObjectId `%s`' % value)
def _serialize(self, value, attr, obj, **kwargs):
if value is None:
return missing
return str(value)```
and I'm getting this error: TypeError: Unknown type for marshmallow model field was used.
Could you give me some directions how to deal with this?
Thanks
Say I have the following snippet:
class WidgetSchema(Schema):
id = fields.UUID()
created_at = fields.DateTime()
foo = fields.String()
baz = fields.Integer()
def create_app():
app = Flask(__name__)
api = Api(app)
@api.route("/widget")
class WidgetResource(Resource):
@accepts(schema=WidgetSchema, api=api)
@responds(schema=WidgetSchema, api=api)
def post(self):
# save data to a new record, and return ID
return request.parsed_obj
@accepts(dict(name="id", type=UUID), schema=WidgetSchema, api=api)
@responds(schema=WidgetSchema, api=api)
def patch(self):
return request.parsed_obj
return app
In this scenario, here are the available fields for each method:
post
patch
id
and created_at
are set by the database, and baz
may only be set during a record creation. Therefore, the model (which describes Widget records), and the acceptable fields for each method are all different.
The default behavior should remain the same. However, the introduction of a new parameter (fields
or something) could provide a list of strings. The strings would be the field names that are allowed for a particular method. If provided, the decorator should ensure that only the fields from that list are included in the request. Additionally, the Swagger UI would only list fields from fields
as available inputs.
The only alternative solution I can see is to create a separate schema for each unique method accept and response (which could become quite verbose).
Here is an example of the same snippet with the new parameters:
class WidgetSchema(Schema):
id = fields.UUID()
created_at = fields.DateTime()
foo = fields.String()
baz = fields.Integer()
def create_app():
app = Flask(__name__)
api = Api(app)
@api.route("/widget")
class WidgetResource(Resource):
@accepts(schema=WidgetSchema, api=api, fields=["foo", "baz"])
@responds(schema=WidgetSchema, api=api)
def post(self):
# save data to a new record, and return ID
return request.parsed_obj
@accepts(dict(name="id", type=UUID), schema=WidgetSchema, api=api, fields=["foo"])
@responds(schema=WidgetSchema, api=api)
def patch(self):
return request.parsed_obj
return app
I'm getting error in my new project using latest flask-restx
flask-accepts 0.16.2 has requirement flask-restx==0.1.0, but you'll have flask-restx 0.2.0 which is incompatible.
Are you going to support multiple versions instead of flask-restx==0.1.0?
Thanks
Hey there.
I would like to add partial: bool param to @accepts
decorator for passing it to schema.load()
I created local branch with those changes, how can I create a PR?
Currently, when I send a post request with invalid data, I'm getting this response
{
"schema_errors": {
"email": [
"Not a valid email address."
],
"password": [
"Missing data for required field."
]
}
}
Is it possible to customize it such that it appears something like this?
{
"message": "Problem occurred",
"errors": {
"email": [
"Not a valid email address."
],
"password": [
"Missing data for required field."
]
}
}
Hi,
i tried to use the flask_accepts extension with marshmallow_sqlalchemy.
Unfortunately it crashes at the utils.py
map_type
function, because its a ModelSchema
or a ModelMetaSchema
.
a quick fix would be adding:
try:
from marshmallow_sqlalchemy import ModelSchema, ModelSchemameta
except ImportError:
pass
and modifing
def map_type(val, api, model_name, operation):
value_type = type(val)
if value_type in type_map:
return type_map[value_type](val, api, model_name, operation)
if isinstance(value_type, SchemaMeta) or isinstance(value_type, Schema) or isinstance(value_type,ModelSchemaMeta) or isinstance(value_type,ModelSchema):
return type_map[Schema](val, api, model_name, operation)
raise TypeError('Unknown type for marshmallow model field was used.')
A short question:
Is it possible to use the two decorators @accepts
and @responds
with schemas defined with flask_marshmellow including the optional integration with flask_sqlalchemy and marshmellow-sqlalchemy?
That way, I could connect marshmallow schemas with models from sqlalchemy and still use your decorators to save some line of codes including swagger documentation.
I pose this question, as I don't see exactly, how you use marshmallow under the hood.
Thanks
The decorators "accepts" and "responds" do not work with Marshmallow schema instances. It will be important to allow that, since you can pass keyword parameters to a schema instance constructor such as "only" and "exclude".
Using a scheme instance with accepts results in:
File "/group13/mattf/git-projects/adfs/venv/lib/python3.6/site-packages/flask_accepts/utils.py", line 32, in for_swagger
for k, v in vars(schema()).get("declared_fields", {}).items()
TypeError: 'MySchema' object is not callable
Using a schema instance with responds results in:
File "/group13/mattf/git-projects/adfs/venv/lib/python3.6/site-packages/flask_accepts/utils.py", line 54, in get_default_model_name
return "".join(schema.__name__.rsplit("Schema", 1))
AttributeError: 'PersonSchema' object has no attribute '__name__'
These should be very easy to fix in the utils module.
Currently, only a single responds
decorator may be provided, optionally with a status_code
. Support for multiple schemas corresponding to different status codes would be straightforward to implement by inspecting the return type and mapping the status_code to the schema, if it is available.
The current functionality for ignoring pre-made Flask Responses would need to be modified to only ignore those for which it does not have a corresponding Schema.
The status code of the response can come from any of the normal Flask means (i.e. as a data, code, headers
tuple, via make_response, etc)
A bug is preventing provided model names from being used
As seen from this answer, marshmallow supports usage of schema instance as nested field of outer schema.
Question in that StackOverflow thread contains one of the examples, when this could be useful and exact use-case we have in our project.
Unfortunately, flask_accepts doesn't support such behaviour at the moment. Usage of schema instance results in Error, thrown in flask_accepts/utils.py at this line of code:
I could submit a fix for that by the end of today. The only change added will be this one:
Note: In the future this could be improved to iteration over type_map keys and ensuring isinstance for all of them.
This would make available usage of marshmallow feature to extend existing marshmallow fields
Using the code from Issue #25 I'm seeing an error thrown in swagger.
Schemas registered with fields.Nested() are not showing up in the swagger ui, and the ui throws the error.
code:
import datetime
from dataclasses import dataclass
import werkzeug
werkzeug.cached_property = werkzeug.utils.cached_property
from marshmallow import fields, Schema, post_load
from flask import Flask, jsonify, request
from flask_accepts import accepts, responds
class CogSchema(Schema):
cog_foo = fields.String(default="cog")
cog_baz = fields.Integer(default=999)
class WidgetSchema(Schema):
foo = fields.String(default="test string")
baz = fields.Integer(default=42)
flag = fields.Bool(default=False)
date = fields.Date(default="01-01-1900")
dec = fields.Decimal(default=42.42)
dct = fields.Dict(default={"key": "value"})
cog = fields.Nested(CogSchema)
def create_app(env=None):
from flask_restplus import Api, Namespace, Resource
app = Flask(__name__)
api = Api(app)
@app.route("/simple/make_a_widget", methods=["POST"])
@accepts(dict(name="some_arg", type=str), schema=WidgetSchema)
@responds(schema=WidgetSchema)
def post():
from flask import jsonify
return request.parsed_obj
@api.route("/restplus/make_a_widget")
class WidgetResource(Resource):
@accepts(dict(name="some_arg", type=str), schema=WidgetSchema, api=api)
@responds(schema=WidgetSchema, api=api)
def post(self):
from flask import jsonify
return request.parsed_obj
return app
app = create_app()
if __name__ == "__main__":
app.run(debug=True)
Hi,
I'm in a situation where I use flask_accepts in association with flask_marshmallow and marshmallow_sqlalchemy.
Basically, all schemas that I define are not constructed from marshmallow base Schema class directly but from another class SQLAlchemySchema (which itself inherits from marshmallow base Schema). In that configuration, when trying to use these Schemas in a Nested field, flask_accepts throws a TypeError as seen in #39: "Unknown type for marshmallow model field was used."
Long story short after looking into it, I can see that SQLAlchemySchema class uses its own metaclass (inherited from marshmallow SchemaMeta) which prevents #40 from working as it uses isinstance
to check the type of the provided Schema.
In example (simplified), we have:
from marshmallow.schema import Schema, SchemaMeta
class SQLAlchemyMeta(SchemaMeta):
...
class SQLAlchemySchema(Schema, metaclass=SQLAlchemyMeta):
...
which leads to the following:
>>> isinstance(type(SQLAlchemySchema), SchemaMeta)
False
and causes the error because type(SQLAlchemySchema)
returns SQLAlchemyMeta
and not SchemaMeta
.
I think we can solve this by using issubclass
instead of isinstance
in #40, which would give:
def map_type(val, api, model_name, operation):
value_type = type(val)
if value_type in type_map:
return type_map[value_type](val, api, model_name, operation)
if issubclass(value_type, SchemaMeta) or issubclass(value_type, Schema):
return type_map[Schema](val, api, model_name, operation)
raise TypeError('Unknown type for marshmallow model field was used.')
I've implemented this change in my project and all seems to work fine.
I'm more than willing to make a PR for this but I'd like to have feedback on my analysis and make sure I did not miss anything.
It would be nice to avoid having to pass api=...
in the decorators for cases where you are attaching swagger documentation to a RESTplus Resource.
The reason for the existing mechanism is that the decorators must introspect to determine if they are being applied to a class method vs a regular function in order to properly register documentation. Perhaps through inspecting local/closure variables within the decorator it may be possible to identify the associated Api, but I'm not completely sure.
It will likely always be necessary to keep the api
parameter around in cases where the default behavior does not function as the user wants, but ideally the default behavior "just works" for most cases and can simplify the flask_accepts api.
Hi,
The handling of the X-Fields
header (or the app.config["RESTX_MASK_HEADER"]
header to be more precise) is ignored when response marshaling is done using a mashmallow
schema.
I have already implemented the bug fix, PR is coming soon.
All the best!
As in the title, it would be very useful to support the Date, DateTime, and Boolean field types. These don't come through in the registered model, and I can see that they are not in the type_map dict in utils.py. There are a great many field types in Marshmallow, some of which may not be relevant to this conversion exercise, but the three I've listed are arguably essential. Where a type is not supported in flask-accepts, I'd say it should default to Raw.
See some preliminary discussion in #15
Marshmallow provides rich validation that, although it can be used in flask_accepts now, does not come with documentation. We can create a mapping to provide Swagger docs on validators that have a corresponding validator in both libraries. The remainder will either get ignored or can be addressed with a lower level solution.
See subject
Model names are showing load and dump extensions after version 0.12.x
class CogSchema(Schema):
cog_foo = fields.String(default="cog")
cog_baz = fields.Integer(default=999)
class WidgetSchema(Schema):
foo = fields.String(default="test string")
baz = fields.Integer(default=42)
flag = fields.Bool(default=False)
date = fields.Date(default="01-01-1900")
dec = fields.Decimal(default=42.42)
dct = fields.Dict(default={"key": "value"})
cog = fields.Nested(CogSchema)
@api.route("/restplus/make_a_widget")
class WidgetResource(Resource):
@accepts(dict(name="some_arg", type=str), schema=CogSchema, api=api)
@responds(schema=CogSchema, api=api)
def get(self):
return request.parsed_obj
@accepts(dict(name="some_arg", type=str), schema=WidgetSchema, api=api)
@responds(schema=WidgetSchema, api=api)
def post(self):
return request.parsed_obj
I took a short look to the decorators and I cannot see a way, to load an added schema partially (therefore ignore required fields) as described in Partial Loading in the marshmallow docs.
An additional arg to the accepts decorator which wil be passed further to the schema.loads methods would make that feature also available in flask_accepts.
I just noticed that my marshmallow Method
fields were not being mapped to the swagger docs. Any chance this was by design?
Hey,
so i want to implement token verification and i require to accept the authentication token as a header
key value pair
i want to validation and document it in the OpenApi specification using marshmallow and accepts
So for requesting an authentication header , how do i declare this in swagger
Restplus provides opportunity to specify if a field in a model is required or not. It also has per-field description, default parameters and other interesting documentation features.
Unfortunately, most of this functionality is not yet supported by flask_accepts, while it might be super-useful for end users to see such information in docs (alongside with fields description, which already has an opened issue #33 ). So I would be happy to participate in developing those features.
As far as I understood, missing 'description, 'required' and other parameters can be fully fixed by modifying
flask_accepts/utils
type_map variable.
I can submit PR with this function:
def _ma_field_to_fr_field(value, model_name) -> dict:
fr_field_parameters = {}
if hasattr(value, 'default'):
fr_field_parameters['example'] = value.default
if hasattr(value, 'required'):
if isinstance(value.required, bool) is False:
raise TypeError(
"Wrong type for 'required' parameter in marshmallow schema field. Model: {}, field: {}".format(
model_name, value.name
)
)
fr_field_parameters['required'] = value.required
if hasattr(value, 'metadata') and 'description' in value.metadata:
fr_field_parameters['description'] = value.metadata['description']
if hasattr(value, 'missing') and type(value.missing) != ma.utils._Missing:
fr_field_parameters['default'] = value.missing
return fr_field_parameters
It can be applied in every mapper function, used to map marshmallow field type to restplus field type in this way:
ma.Bool: lambda val, api, model_name, operation: fr.Boolean(
**_ma_field_to_fr_field(val, model_name)
),
It should also be applied to Nested and List types in the same way:
fr.List(
map_type(val.inner, api, model_name, operation), **_ma_field_to_fr_field(val, model_name)
)
fr.Nested(
map_type(val.nested, api, model_name, operation), **_ma_field_to_fr_field(val, model_name)
)
It can later be used to parse marshmallow model fields for Range validator with min / max being present. This might be useful as restplus allows to specify min / max values for List, String, Float and other types.
The result documentation for models would look like this:
For comparison, without this fix, documentation for same model would look like this:
If this whole thing makes sense to lead developers on this project, I am happy to add some tests to new functionality and submit PR by the end of today.
Small Note: I have been working with restplus a lot in scope of my current project at work. Unfortunately, me and my team realised restplus limits us too much in terms of model parsing. That is why we decided to move to Marshmallow. However, we want to keep documentation and are planning to use flask_accepts to do so. That is why I will be happy to submit other changes in future.
Example code:
@api.route('/user')
class UserAPIResource(Resource):
'''REST API with get, put, post...'''
@responds(schema=UserSchema(), api=api, status_code=200)
def get(self):
"""
return current user info that gets used by various services
"""
data = get_user_data(user_id=current_user.id)
# data is a dict here, like {"email": "...", etc...}
return data
Crashes:
TypeError: 'dict' object is not callable
The view function did not return a valid response. The return type must be a string, tuple, Response instance, or WSGI callable, but it was a dict.
It's because of the following part in decorators.py
if not _is_method(func): # <- this fails
return jsonify(serialized), status_code
# and we hit this line which DOES NOT use jsonify
# although flask expects somethign wrapped in jsonify here...
return serialized, status_code
I have a case where I defined a marshmallow schema that is being used in both @accepts and @responds for different methods. I would like to use the schema object in an @accepts for a get_all route but instead of the fields being defined in the body of the request they should be query params. For example a UserSchema might have 3 fields.
class UserSchema(Schema):
userId = ma_fields.Integer(dump_only=True, attribute='user_id')
firstName = ma_fields.String(attribute='first_name')
emailAddress = ma_fields.Email(attribute='email')
and we might have a get_all route like this
@accepts(dict(name='limit', type=int), dict(name='page', type=int), api=api)
@responds(schema=UserSchema(many=True), api=api)
def get(self):
return UserService.get_all(**request.parsed_args)
I would like to have a route with documented parameters based on the schema
Perhaps something like
@accepts(dict(name='limit', type=int), dict(name='page', type=int), UserSchema(location='values'), api=api)
@responds(schema=UserSchema(many=True), api=api)
def get(self):
return UserService.get_all(**request.parsed_args)
It seems like if we are returning a UserSchema object/s with this request we should be able to easily make all of that schemas dump-able fields queryable.
End result would document the existence of ?userId ?firstName and ?emailAddress.
and request.parsed_args would be a dict with 'user_id', 'first_name', 'email' as well as the other query arg dicts 'limit' and 'page'
Hi!
I am using flask_accepts
together with flask_restplus
.
To do so, I need to change some code of this library.
My question is that, with which is this library intended to be used, flask_RESTful
or flask_restplus
?
In order to avoid circular import in a flask/marshmallow project, it's possible to reference the Nested field by using its name (as described here https://marshmallow.readthedocs.io/en/latest/nesting.html#two-way-nesting)
Unfortunately, flask_accepts doesn't support it :
File env/lib/python3.8/site-packages/flask_accepts/decorators/decorators.py", line 110, in decorator
body = for_swagger(
File "env/lib/python3.8/site-packages/flask_accepts/utils.py", line 63, in for_swagger
fields = {
File "env/lib/python3.8/site-packages/flask_accepts/utils.py", line 64, in
k: map_type(v, api, model_name, operation)
File "env/lib/python3.8/site-packages/flask_accepts/utils.py", line 182, in map_type
return type_map[value_type](val, api, model_name, operation)
File "env/lib/python3.8/site-packages/flask_accepts/utils.py", line 19, in unpack_nested
model_name = get_default_model_name(val.nested)
File "env/lib/python3.8/site-packages/flask_accepts/utils.py", line 152, in get_default_model_name
return "".join(schema.name.rsplit("Schema", 1))
AttributeError: 'str' object has no attribute 'name'
A change will also be necessary for the method map_type() since you have to give the object iteself while it's not yet charged.
Can you please add the possibility to support this configuration ?
Thank you
Hello @apryor6 .
I am happy using this library, but sometimes I want to do something before validation, for example pass context to Marshmallow Schema, but I still want to generate swagger for this API.
I'd like to contribute to the library implementing such functionality. What is the best way to do it to your opinion?
accepts
and responds
decorators with validate: bool
param.@swagger
just for generation swagger spec.What do you think?
when add accepts like this:
@accepts(dict(name='file',type=FileStorage, help='select file', location='files'),api=api)
the swagger doc is not correct.
In decorators.py line 56:
for qp in query_params: _parser.add_argument(**qp, location="values")
I changed to:
for qp in query_params: if(qp.get('location')): _parser.add_argument(**qp) else: _parser.add_argument(**qp,location="values")
then swagger works.
I am using flask-restx==0.2.0 with flask_accepts==0.16.4
Specifying default
as a non serializable value like datetime
in Marshmallow causes the Swagger page to fail to load. Here is my code:
class RespondSchema(Schema):
data = fields.String(required=True)
timestamp = fields.DateTime(default=datetime.utcnow)
@api.route("/route")
class SomeRoute(Resource):
@api.doc("some_route")
@accepts(schema=AcceptSchema, api=api)
@responds(schema=RespondSchema, api=api)
def post(self):
return SomeService.do_stuff(request.parsed_obj)
Traceback (most recent call last):
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask/app.py", line 2464, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/werkzeug/middleware/proxy_fix.py", line 169, in __call__
return self.app(environ, start_response)
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask/app.py", line 2450, in wsgi_app
response = self.handle_exception(e)
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask_restx/api.py", line 638, in error_router
return original_handler(f)
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask_cors/extension.py", line 161, in wrapped_function
return cors_after_request(app.make_response(f(*args, **kwargs)))
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask/app.py", line 1867, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask_restx/api.py", line 636, in error_router
return self.handle_error(e)
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app
response = self.full_dispatch_request()
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask_restx/api.py", line 638, in error_router
return original_handler(f)
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask_cors/extension.py", line 161, in wrapped_function
return cors_after_request(app.make_response(f(*args, **kwargs)))
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask/app.py", line 1821, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask_restx/api.py", line 636, in error_router
return self.handle_error(e)
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask/app.py", line 1950, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask/app.py", line 1936, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask_restx/api.py", line 379, in wrapper
return self.make_response(data, code, headers=headers)
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask_restx/api.py", line 402, in make_response
resp = self.representations[mediatype](data, *args, **kwargs)
File "/Users/andrew.mickael/PycharmProjects/Financial-Ingestion-Service/venv/lib/python3.7/site-packages/flask_restx/representations.py", line 25, in output_json
dumped = dumps(data, **settings) + "\n"
File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/__init__.py", line 238, in dumps
**kw).encode(obj)
File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 201, in encode
chunks = list(chunks)
File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 431, in _iterencode
yield from _iterencode_dict(o, _current_indent_level)
File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 405, in _iterencode_dict
yield from chunks
File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 405, in _iterencode_dict
yield from chunks
File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 405, in _iterencode_dict
yield from chunks
File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 405, in _iterencode_dict
yield from chunks
File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 405, in _iterencode_dict
yield from chunks
File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 438, in _iterencode
o = _default(o)
File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type builtin_function_or_method is not JSON serializable
Hi,
I tried to use the decorator @accepts with a marshmallow schema, but there is an error:
File "/XXX/YYY//site-packages/flask_accepts/decorators/decorators.py", line 80, in inner
obj, err = schema(many=many).load(request.get_json())
ValueError: too many values to unpack (expected 2)
I think, this is because marshmallow's schema.load returns from version 3.0.0b7 only one parameter, not two (https://marshmallow.readthedocs.io/en/stable/upgrading.html).
Is there any way to cover this?
Output data is not validated which means that the generated Swagger API docs might be outdated / out of sync with the actual data returned by API.
Explanation:
@responds
uses Marshmallow schema.dumps
which does not validate.
https://stackoverflow.com/questions/34191547/marshmallow-not-giving-errors
Hello I found that dump_only and load_only arguments in marshmallow fields are ignored in swagger and all fields are mentioned instead.
Sent with GitHawk
Currently, the model name can be specified in either accepts
or responds
; however, in the case where both decorators are applied there is a "last in" effect where the top-most decorator (in code) are the only models that are displayed in the documentation. This affects only the documentation and not the functionality of the api itself.
It would be preferable to allow for declaration of models at both places or, at least, to be able to introspect in the case where both decorators are provided but the name is given in the lower decorator.
Currently, only content-type:json
is supported in @accepts as per code here.
In my use-case, request schema has a file and other form parameters. Since flask keeps that data in request.values
and request.form
attributes it is currently not configurable.
My suggestion for this is to check the headers and parse the parameters accordingly. Please let me know If there is another way to get around it or this can be the right direction going forward. And If it is, I can pitch in to create a PR for it.
See this issue for a discussion around how RESTplus parses the string "false" as true with type=bool
.
We can fix this by intercepting the boolean parameter in the dicts that are being passed in @accepts
or @reponds
and casting them to flask_restplus.inputs.boolean
Using a marshmallow schema with many=True
seems to be ignored in both validation and the swagger output.
my_ns = my_api.namespace("roles")
@my_ns.route("/")
class MyResource(flask_restplus.Resource):
@flask_accepts.accepts(schema=MyRequestSchema(many=True), api=my_ns)
@flask_accepts.responds(schema=MyResponseSchema(many=True), api=my_ns)
def put():
return []
For this example, the endpoint will not accept a list at the top-level of the request body as I intend. It just accepts an object as if many=True
wasn't there.
I believe in versions prior to 0.14.2 the validation worked, but the swagger output was still incorrect.
Hi.
After cloning the project and installing requirements, I tried to run the tests only for them to fail. I then realized that this library uses dataclasses
which is a python >= 3.8 feature. Would be nice to add this information to the README file. It would really help with those interested in contributing ๐
.
Hi,
Just for the sake of completeness, the accepts
and responds
decorators should accept dictionaries (with a model_name
argument) as a schema. To achieve this, model_name
could be passed on to _get_or_create_schema()
from the decorators and the method should be extended with this bit of code:
if isinstance(schema, dict):
if model_name is None:
raise ValueError(
"You should set model_name when the schema is generated from a dict."
)
schema = Schema.from_dict(schema, name=model_name)
I can send a PR if the feature request is accepted.
Br,
This lib is really nice and works great with simple marshmallow schemas. Unfortunately Swagger doesn't display correctly when using nested schemas like this
According to restPlus documentaion:
Use the location argument to add_argument() to specify alternate locations to pull the values from(https://flask-restplus.readthedocs.io/en/stable/parsing.html)
From the request headers
parser.add_argument('User-Agent', location='headers')
So restPlus parser can validate headers if it is necessary, but if I use @accepts deco it does not validate headers. See example below:
class Todo(Resource):
@accepts(
{'name': 'id', 'type': str, 'required': True, 'action': 'append'},
{'name': 'fake_header', 'type': str, 'required': True, 'location': 'headers'}, # verify header
schema=SomeSchema,
api=api
)
def post(self):
return {"method": 'Fake header is not validated!'}
And when I trigger the POST method without fake_header
it returns me response (200 OK), but I expect 400 Bad request
.
It looks like you somehow override default behavior of reqparse
module.
I am trying to add a string field to my Schema which uses the validate.OneOf() from marshmallow. I see the correct error response if I pass a bad option to this field but the Swagger UI does not have the helpful options in model description. Are there plans to add maps from the native marshmallow validators to the supported Flask-Restplus validators.
I was able to achieve this behavior using the api.model from RestplusUsing the Swagger interface i'm trying to make a PUT request. This triggers the update but i get an error in the response and in the console where i'm running the server.
Response error:
{
"message": "Internal Server Error"
}
I'm using Flask_api_example, the tests all pass fine but making a PUT request in the swagger interface (127.0.0.1:5000) triggers this error. The entry gets updated just fine, couldn't find the issue in my implementation so i think it can be related to flask_accepts.
127.0.0.1 - - [12/Nov/2019 20:22:47] "GET /api/barbota/ranks/ HTTP/1.1" 200 -
[2019-11-12 20:22:57,833] ERROR in app: Exception on /api/barbota/ranks/1 [PUT]
Traceback (most recent call last):
File "/home/bledy/projects/barbota-api/.venv/lib/python3.7/site-packages/flask/app.py", line 1949, in full_dispatch_request
rv = self.dispatch_request()
File "/home/bledy/projects/barbota-api/.venv/lib/python3.7/site-packages/flask/app.py", line 1935, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/home/bledy/projects/barbota-api/.venv/lib/python3.7/site-packages/flask_restplus/api.py", line 325, in wrapper
resp = resource(*args, **kwargs)
File "/home/bledy/projects/barbota-api/.venv/lib/python3.7/site-packages/flask/views.py", line 89, in view
return self.dispatch_request(*args, **kwargs)
File "/home/bledy/projects/barbota-api/.venv/lib/python3.7/site-packages/flask_restplus/resource.py", line 44, in dispatch_request
resp = meth(*args, **kwargs)
File "/home/bledy/projects/barbota-api/.venv/lib/python3.7/site-packages/flask_accepts/decorators/decorators.py", line 100, in inner
return func(*args, **kwargs)
File "/home/bledy/projects/barbota-api/.venv/lib/python3.7/site-packages/flask_accepts/decorators/decorators.py", line 186, in inner
serialized = schema.dump(rv)
File "/home/bledy/projects/barbota-api/.venv/lib/python3.7/site-packages/marshmallow/schema.py", line 553, in dump
result = self._serialize(processed_obj, many=many)
File "/home/bledy/projects/barbota-api/.venv/lib/python3.7/site-packages/marshmallow/schema.py", line 517, in _serialize
value = field_obj.serialize(attr_name, obj, accessor=self.get_attribute)
File "/home/bledy/projects/barbota-api/.venv/lib/python3.7/site-packages/marshmallow/fields.py", line 325, in serialize
return self._serialize(value, attr, obj, **kwargs)
File "/home/bledy/projects/barbota-api/.venv/lib/python3.7/site-packages/marshmallow/fields.py", line 899, in _serialize
ret = self._format_num(value) # type: _T
File "/home/bledy/projects/barbota-api/.venv/lib/python3.7/site-packages/marshmallow/fields.py", line 874, in _format_num
return self.num_type(value)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'InstrumentedAttribute'
after upgrade to latest version(0.7.1) occured this exception
README.md contains the following example, but it looks to me like there are some issues with this code. I'd fix it, but I'm not 100% sure what the best fix is. Happy to raise a PR if you're able to point me in the right direction.
The code looks like this...
from flask import Flask, request
from flask_accepts import accepts, responds
from .widget import Widget, WidgetSchema, make_widget
def create_app():
app = Flask(__name__)
@app.route("/widget")
@accepts(dict(name="foo", type=str), api=api)
@responds(schema=WidgetSchema, api=api)
def widget()
name: str = request.parsed_args["foo"]
widget: Widget = make_widget(name)
return widget
return app
Issues:
def widget()
is missing the :
. Easy fix.@accepts
decorator uses api
but this isn't defined within this code. Is the fix to remove this from the decorator?@responds
decorator has same issue. Same fix?I'm attempting to run your marshmallow_example.py example, but keep running into this error:
...
File "/Users/SomeProject/venv/lib/python3.7/site-packages/marshmallow/schema.py", line 835, in _do_load
partial=partial,
File "/Users/SomeProject/venv/lib/python3.7/site-packages/marshmallow/schema.py", line 1014, in _invoke_load_processors
partial=partial,
File "/Users/SomeProject/venv/lib/python3.7/site-packages/marshmallow/schema.py", line 1135, in _invoke_processors
data = processor(data, many=many, **kwargs)
TypeError: make() got an unexpected keyword argument 'many'
I also had to update the get
method to post
in this block to get past the error:
TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body.
@api.route("/restplus/make_a_widget")
class WidgetResource(Resource):
@accepts(
"Doodad", dict(name="some_arg", type=str), schema=DoodadSchema, api=api
)
@responds("Widget", schema=WidgetSchema, api=api)
def post(self):
from flask import jsonify
return request.parsed_obj
In flask-resplus marshall, I can use:
@api.marshal_with(User, envelope="data")
I don't find this in response. I would like to use something like:
@responds(schema=User, api=api, many=True, envlope='data')
I also can't do something like:
users = user_service.get_users()
return {"data": users}
My current workaround is to use @api.expect(User)
(to include those param into swagger doc) and schema.dump()
@api.expect(User)
def get(self):
"""List all available users"""
try:
users = user_service.get_users()
return {"data": user_schema.dump(users)}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.