marshmallow-code / flask-smorest Goto Github PK
View Code? Open in Web Editor NEWDB agnostic framework to build auto-documented REST APIs with Flask and marshmallow
Home Page: https://flask-smorest.readthedocs.io
License: MIT License
DB agnostic framework to build auto-documented REST APIs with Flask and marshmallow
Home Page: https://flask-smorest.readthedocs.io
License: MIT License
Hello, i have just updated from version 0.12.0 and experiencing following issue:
127.0.0.1 - - [24/Feb/2019 12:06:49] "GET /api/endpoint HTTP/1.1" 500 -
Traceback (most recent call last):
File "/Users/user/git/project/env/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
raise value
File "/Users/user/git/project/env/lib/python3.7/site-packages/flask/cli.py", line 76, in find_best_app
app = call_factory(script_info, app_factory)
File "/Users/user/git/project/env/lib/python3.7/site-packages/flask/cli.py", line 116, in call_factory
return app_factory()
File "/Users/user/git/project/app.py", line 36, in create_app
api.init_app(app)
File "/Users/user/git/project/api/__init__.py", line 10, in init_app
api.init_app(app)
File "/Users/user/git/project/env/lib/python3.7/site-packages/flask_rest_api/__init__.py", line 61, in init_app
self._init_spec(**(spec_kwargs or {}))
File "/Users/user/git/project/env/lib/python3.7/site-packages/flask_rest_api/spec/__init__.py", line 193, in _init_spec
self.spec.components.schema(name, schema=schema_cls, **kwargs)
File "/Users/user/git/project/env/lib/python3.7/site-packages/apispec/core.py", line 141, in schema
ret.update(plugin.schema_helper(name, component, **kwargs) or {})
File "/Users/user/git/project/env/lib/python3.7/site-packages/apispec/ext/marshmallow/__init__.py", line 158, in schema_helper
json_schema = self.openapi.schema2jsonschema(schema_instance)
File "/Users/user/git/project/env/lib/python3.7/site-packages/apispec/ext/marshmallow/openapi.py", line 645, in schema2jsonschema
jsonschema = self.fields2jsonschema(fields, partial=partial, ordered=ordered)
File "/Users/user/git/project/env/lib/python3.7/site-packages/apispec/ext/marshmallow/openapi.py", line 669, in fields2jsonschema
property = self.field2property(field_obj)
File "/Users/user/git/project/env/lib/python3.7/site-packages/apispec/ext/marshmallow/openapi.py", line 421, in field2property
ret["items"] = self.field2property(field.container)
File "/Users/user/git/project/env/lib/python3.7/site-packages/apispec/ext/marshmallow/openapi.py", line 415, in field2property
schema_dict = self.resolve_nested_schema(field.schema)
File "/Users/user/git/project/env/lib/python3.7/site-packages/marshmallow/fields.py", line 447, in schema
dump_only=self._nested_normalized_option('dump_only'),
TypeError: __init__() missing 1 required positional argument: 'cls_or_instance'
I've seen issue #19 and the recomendation instead of having more than one response is to use @blp.doc
The issue with this a approach is that it doesn't support any Schema information.
If we add schema as the object it doesn't work because it's not being replaced by it's json-schema's $ref
@blp.doc(responses={
'401': {'schema': GenericErrorSchema, 'description': 'Invalid credentials'}
})
On the other hand when setting more than one @blp.response
the openapispec is ok because it's using the autogenerated definition. The only issue is on the response serialization wrapper
Lot's of ideas come to my mind to fix this, but what do you think?
@blp.response
with some extra param like only_doc=True
@blp.response_doc
to the ResponseMixin
where wrapper
just returns result_raw
@blp.doc(responses=...)
with GenericErrorSchemaGenericErrorSchema
json encode into a $ref
dict by calling something like OpenAPIConverter.get_ref_dictSee #67 (comment).
Maybe we could add the whole mapping from https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md.
Care should be taken about retro-compatibility. I don't use swagger-ui and I don't know what happens when feeding an old version with a more recent parameter.
OpenAPI v3 supports dictionaries to be specified with a key / value type. The following Marshamallow classes produce JSON without that information
@api.schema('Output')
class OutputSchema(ma.Schema):
class Meta:
strict = True
min = ma.fields.Number()
max = ma.fields.Number()
@api.schema('OutputDict')
class OutputDictSchema(ma.Schema):
class Meta:
strict = True
data = ma.fields.Dict(keys=ma.fields.String(), values=ma.fields.Nested(OutputSchema))
Should produce something like this:
"Output": {
"type": "object",
"properties": {
"min": {
"type": "number"
},
"max": {
"type": "number"
}
}
},
"OutputDict": {
"type": "object",
"properties": {
"data": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"text": {
"$ref": "#/components/schemas/Output"
}
}
}
}
}
When using a parameter in a route: @bp.route('/<string:foo_id>')
The openapi.json generated is not compatible with openapi 3.0:
"parameters": [
{
"in": "path",
"name": "foo_id",
"required": true,
"type": "string"
}
]
should be:
"parameters": [
{
"in": "path",
"name": "bar_id",
"required": true,
"schema": {
"type": "string"
}
}
]
I am not sure if this is a flask-rest-api issue, or something to fix in a dependency… But I would be happy to take stab at a PR, if pointed in the right direction (I couldn't figure where in blueprint.py
that part of the doc was generated).
I like using as_kwargs
, coming from flask-apispec which has a @use_kwargs
decorator.
Feature request: have a decorator that has as_kwargs=True
so I don't have to type it every time
Thanks!
Is it possible to use @blp.response
multiple times to document/serialize different HTTP status codes?
Hi,
I believe that due to recent updates in both apispec
and marshmallow
my application started to throw warnings and exceptions (e.g., marshmallow.exceptions.RegistryError
, apispec.exceptions.DuplicateComponentNameError
) that were not happening a few weeks ago.
I couldn't came up with a minimal example to illustrate this yet. But I wonder if you could provide an example (beyond the basic "Petstore example" from the docs) of how to structure a more realistic application (like using the application factory pattern, splitting schemas and views in different modules, etc).
Regards,
The Blueprint.doc
decorator is meant to add documentation that can't be inferred from the code or added by dedicated decorators. It allows the user to pass manual documentation that will be deep-merged with the automatically generated documentation. Unfortunately, it is not the silver bullet one would expect. It suffers from a few intrinsic issues.
We could try to play smart and improve the way the structure received by doc
is used, but I'd rather add dedicated decorators or other explicit means to allow the user to pass documentation in a structured fashion, with a clear and thin abstraction layer.
I'd like to use this issue to list known issues about Blueprint.doc
and use cases that should be covered by other means. The goal being to make it useless, and to deprecate it if possible.
response
decorator (#60 (comment)).['content']['application/json']
when using v3 which makes it more verbose to the user.response
/arguments
decorators._apidoc
attribute mechanism becomes public API.Blueprint.route
. (See #23)Hi,
I'm creating an app with many blueprints for different sections of the API. It seems there should be a way to define definitions inside the blueprint. Something like:
blp = Blueprint(
'pets', 'pets', url_prefix='/pets',
description='Operations on pets'
)
@blp.definition('Pet')
class PetSchema(ma.Schema):
class Meta:
strict = True
ordered = True
id = ma.fields.Int(dump_only=True)
name = ma.fields.String()
It's not suitable to create a circular dependency where the API is defined so it seems you need to do something like:
api=Api()
@api.definition('Pet')
Any recommendations on workarounds? thanks!
I need to set cookies with on some endpoints. Just returning jsonify({ 'key': 'value' })
does not get serialized. Is there a way to set a cookie in the response together with the "normal" endpoint response?
I use flask_classful in some of my projects, and it would be great if it was supported.
I'll need to dig into the code a bit more, but I'm fairly certain flask_classful's main export, FlaskView
, is fairly similar in implementation to the now standard Flask.MethodView
(which is already supported).
Would this be something that could be integrated into the project?
I have an endpoint that allows the end user to specify which fields they want returned, which I pass through to the Marshmallow serializer. This is pretty easy to do:
blp = Blueprint('people', 'people', url_prefix='/people',
description='Operations on people')
class PersonGetArgsSchema(Schema):
"""Query arg schema for people get endpoint"""
id = fields.String(required=True)
return_fields = fields.List(fields.String(), required=True)
@api.definition('Person')
class PersonSchema(Schema):
"""Marshmallow schema for serializing a person instance"""
id = fields.String(required=True, example='3065ac69-6aad-4832-9889-c81ab773424e')
# other fields
@blp.route('/get')
class PersonResource(MethodView):
@blp.arguments(PersonGetArgsSchema, location='json')
@blp.response(None)
def post(self, body):
"""Get a single person by ID (actually using POST, since REPORT isn't supported)"""
# get the person from Elasticsearch
person = PersonDoc.get(id=body['id'], _source=body['return_fields'])
# serialize and return
person_schema = PersonSchema(only=body['return_fields'])
return person_schema.dump(person.to_dict())
This works fine, but because I specified @blp.response(None)
, my API docs don't automatically have an example response like I would if I had schema=PersonSchema
.
My current solutions is to subclass Schema
:
class SchemaWithEx(Schema):
def example_dict(self):
return {
k: v.metadata['example'] for k, v in self.fields.items() if 'example' in v.metadata
}
@api.definition('Person')
class PersonSchema(SchemaWithEx):
# ...
@blp.response(schema=None, example=PersonSchema(only=['id', 'name']).example_dict())
Is my situation really obscure or would it be worth adding a feature like this?
I think this is more of a webargs issue than specific to flask-rest-api
, but I also suspect that the most efficient way to address it, would be inside the API…
In case an invalid JSON input is provided (to a POST
or PUT
call), it is treated as an empty dict {}
, no error is thrown, and, depending on the value of model_build_obj
, it is either:
From what I understand, this is due to webargs' flask parser loading the JSON silently:
# Fail silently so that the webargs parser can handle the error
if hasattr(req, "get_json"):
# Flask >= 0.10.x
json_data = req.get_json(force=force, silent=True)
else:
# Flask <= 0.9.x
json_data = req.json
if json_data is None:
return core.missing
… from the comment in webarg's code, it seems the user is supposed to implement error handling to catch this case, although I don't quite see how it would be possible to know retrospectively that the core.missing
being returned was the reason of an invalid JSON, rather than actually missing…
Do you have any idea how I could go about ensuring my API returns a proper 400 error?
Errors triggered by abort(code)
are logged as INFO
. This is to high.
I suggest
DEBUG
ERROR_LOGGING_LEVEL
of ErrorHandlerMixin
If this is still not enough, people can override _log_error
.
Hi,
First of all, thanks for such a great framework. I started using flask-rest-api only a few days ago and my API looks much better now.
I report this issue mostly for documentation purpose because I believe it's fixed with this commit but I thought it may be helpful for others until the next release of flask-rest-api.
Using flask-rest-api 0.9.0 with apispec 1.0.0b3, if you try to access the openapi.json file you will get the following error:
127.0.0.1 - - [10/Oct/2018 19:55:28] "GET /openapi/openapi.json HTTP/1.1" 500 -
Traceback (most recent call last):
File "/Users/sancho/.local/share/virtualenvs/preciosa--GgWvvys/lib/python3.7/site-packages/flask/app.py", line 2309, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/sancho/.local/share/virtualenvs/preciosa--GgWvvys/lib/python3.7/site-packages/flask/app.py", line 2295, in wsgi_app
response = self.handle_exception(e)
File "/Users/sancho/.local/share/virtualenvs/preciosa--GgWvvys/lib/python3.7/site-packages/flask/app.py", line 1741, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/sancho/.local/share/virtualenvs/preciosa--GgWvvys/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
raise value
File "/Users/sancho/.local/share/virtualenvs/preciosa--GgWvvys/lib/python3.7/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/Users/sancho/.local/share/virtualenvs/preciosa--GgWvvys/lib/python3.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/sancho/.local/share/virtualenvs/preciosa--GgWvvys/lib/python3.7/site-packages/flask/app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/sancho/.local/share/virtualenvs/preciosa--GgWvvys/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
raise value
File "/Users/sancho/.local/share/virtualenvs/preciosa--GgWvvys/lib/python3.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/sancho/.local/share/virtualenvs/preciosa--GgWvvys/lib/python3.7/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/sancho/.local/share/virtualenvs/preciosa--GgWvvys/lib/python3.7/site-packages/flask_rest_api/spec/__init__.py", line 123, in _openapi_json
json.dumps(self.spec.to_dict(), indent=2),
File "/Users/sancho/.local/share/virtualenvs/preciosa--GgWvvys/lib/python3.7/site-packages/apispec/core.py", line 118, in to_dict
info.setdefault('title', self.title)
AttributeError: 'NoneType' object has no attribute 'setdefault'
To solve this issue, you either install flask-rest-api from github (after this commit) or you must do one of these:
In your config.py,
API_SPEC_OPTIONS = {
'info': {
'title': 'API Test',
'version': 'v1'
}
}
or
api = Api(app, spec_kwargs={'info': {'title': 'API Test', 'version': 'v1'}})
or using the factory pattern
api = Api()
def create_app():
app = Flask(__name__)
...
api.init_app(app, spec_kwargs={'info': {'title': 'API Test', 'version': 'v1'}})
I'm using marshmallow v3-rc5 and using two-way nesting
Using this technique I get the following error if I attempt to use something like @blp.response(CreatorSchema(many=True, exclude=('follower_count',)))
:
/Users/cyber/.virtualenvs/luve-pim5aQIP/lib/python3.7/site-packages/apispec/ext/marshmallow/common.py:143:
UserWarning: Multiple schemas resolved to the name Creator. The name has been modified. Either manually add each of the schemas with a different name or provide a custom schema_name_resolver.
and see multiple versions of the schema in swagger (Creator, Creator1).
If I remove the exclude
arg to my schemas and make new schemas then everything works perfectly. Something about exclude
causes it to think there are multiple versions of the schema and it all explodes.
I'd like to define multiple response codes for a view (201 for created, 200 for updated).
It seems that decorating a view twice will always return 200:
@thing_bp.response(ThingSchema, code=200, description='Thing was updated')
@thing_bp.response(ThingSchema, code=201, description='Thing was created')
def post(...)
Is there a way around this?
Thanks
Hi,
First of all, thank you so much for this awesome extension! It really fullfills what I need.
I am just facing a little problem. At first i thought it was because i was using a application factory, but then i tried this simple code and I could not generate the openapi.json.
It throws the error TypeError: Object of type 'PetSchema' is not JSON serializable.
It sources back to
File "/flask_app/ext/flask_rest_api/spec/__init__.py", line 78, in _openapi_json
def _openapi_json(self):
"""Serve JSON spec file"""
# We don't use Flask.jsonify here as it would sort the keys
# alphabetically while we want to preserve the order.
return current_app.response_class(
json.dumps(self.to_dict(), indent=2),
mimetype='application/json')
Basically json.dumps fails because it is passed the Marshmallow schema.
Could you please tell me what I do wrong?
Merci beaucoup !!!
app.py:
import marshmallow as ma
from flask import Flask
from flask.views import MethodView
from flask_rest_api import Api, Blueprint
from .config import Config
app = Flask('My API')
app.config.from_object(Config)
api = Api(app)
class Pet:
pass
api.definition('Pet')
class PetSchema(ma.Schema):
class Meta:
strict = True
ordered = True
id = ma.fields.Int(dump_only=True)
name = ma.fields.String()
class PetQueryArgsSchema(ma.Schema):
class Meta:
strict = True
ordered = True
name = ma.fields.String()
blp = Blueprint(
'pets', 'pets', url_prefix='/pets',
description='Operations on pets'
)
@blp.route('/')
class Pets(MethodView):
@blp.arguments(PetQueryArgsSchema, location='query')
@blp.response(PetSchema(many=True))
def get(self, args):
"""List pets"""
return Pet.get(filters=args)
api.register_blueprint(blp)
config.py
API_VERSION = '1.0.0'
OPENAPI_VERSION = '3.0.0'
OPENAPI_URL_PREFIX = "/apidoc/"
OPENAPI_JSON_PATH = 'openapi.json'
OPENAPI_SWAGGER_UI_PATH = "swagger-ui"
OPENAPI_SWAGGER_UI_VERSION = '3.18.2'
requirements.txt
Flask==1.0.2
flask-rest-api==0.7.0
apispec==0.39.0
itsdangerous==0.24
Jinja2==2.10
MarkupSafe==1.0
marshmallow==2.15.4
PyYAML==3.13
webargs==4.0.0
Werkzeug==0.14.1
Hello,
I am just starting out with building REST Api in general, so not sure if I am missing something while using this library.
I used the example from the documentation; but when I try to access the openapi.json
, I get the error mentioned in the description. Is there anything missing with the following code?
@api.definition('Pet')
class PetSchema(ma.Schema):
class Meta:
strict = True
ordered = True
id = ma.fields.Int(dump_only=True)
name = ma.fields.String()
Also, if you could provide a complete working example, including a working openapi-ui endpoint, that'd be really helpful.
Thanks
Same as flask_apispec's issue: https://github.com/jmcarp/flask-apispec/pull/125/files
The application may be deployed under a path, such as when deploying as a serverless lambda. The path needs to be prefixed.
Any idea how to add content-negotiation to an endpoint, in a way similar to what is described here for flask-restful:
https://flask-restful.readthedocs.io/en/latest/extending.html#content-negotiation
(also: is the project still being maintained? No update in a while…)
Ideally, when an endpoint call abort
with an HTTP error, we would like the message to be filled according to the endpoint's documentation (e.g. "Resource missing", rather than Flask's default "The requested URL was not found on the server" for a 404).
This seems like something that could be done in the abort
method? I have had a look at the Blueprint class, but still unsure where it should look, to get the list of responses for that particular call…
When I started using this library I wrote, out of habit, something like this:
@bp.response(code=HTTPStatus.NO_CONTENT)
(In my opinion using htttp.HTTPStatus is a good think because it prevents Magic Number anti-pattern)
It looked fine, but after a while i realized that instead of resulting in:
"responses": {
"204": {}
}
It is rendered as:
"responses": {
"HTTPStatus.NO_CONTENT": {}
}
So my suggestion is to add int()
to this line:
https://github.com/Nobatek/flask-rest-api/blob/92229aaf74819ba9a87ff98d7888ca25661d7e4e/flask_rest_api/response.py#L49
Hi!
I want to start off with saying I am really thankful for work on this library.
The way I prefer structuring my flask app is via application factory but defining a schema, as suggested in the documentation, requires an instance of flask_rest_api.Api
. I suggest creating a method in called register_schema
an alternative to the schema
decorator. Usage will be similar to register_blueprints
.
from flask import Flask
from flask_rest_api import Api
from . import schemas
def create_app(config):
app = Flask(__name__)
api = Api(app)
register_api_schemas(api)
register_api_blueprint(api)
return app
def register_schemas(api):
api.register_schema("Foo", schemas.FooSchema)
api.register_schema("Bar", schemas.BarSchema)
My current work around is something like
def register_schemas(api):
api.schema("Foo")(schemas.FooSchema)
api.schema("Bar")(schemas.BarSchema)
Problem: register_blueprint method in Api does not takes "options" argument and so it is impossible to pass options to blueprint.
Current Api.register_blueprint without options support:
https://github.com/Nobatek/flask-rest-api/blob/master/flask_rest_api/__init__.py#L62
Flask's register_blueprint documentation with mentioned "options": http://flask.pocoo.org/docs/1.0/api/?highlight=blueprint#flask.Flask.register_blueprint
Blueprint register documentation:
http://flask.pocoo.org/docs/1.0/api/?highlight=blueprint#flask.Blueprint.register
It would be helpful to be able to use "options".
Hey guys,
Interesting project you have here; are there any plans on how to actually use it?
Thanks!
Diogo
Hi,
I believe that due to recent changes around the Blueprint.doc decorator it's not possible to have 2 flask applications created at the same time. See below a minimal example. Tests should pass with flask-rest-api 0.10 but the test test_app
fails with 0.11. Is this the expected behavior or it's a bug?
### app.py ###
from flask import Flask
from flask.views import MethodView
from flask_rest_api import Api, Blueprint
rest_api = Api()
_ = Blueprint('Test', __name__, url_prefix='/api/test')
@_.route('/')
class TestView(MethodView):
@_.response()
def get(self):
return [1,2,3]
def create_app():
app = Flask(__name__)
rest_api.init_app(app)
rest_api.register_blueprint(_)
return app
### test_app.py ###
from app import create_app
def test_app():
app = create_app()
# This will fail with flask-rest-api >= 0.11
app2 = create_app()
Here's the output from pytest
$ pytest
============================================================================ test session starts ============================================================================
platform darwin -- Python 3.7.0, pytest-4.0.0, py-1.7.0, pluggy-0.8.0
rootdir: /Users/sancho/Development/test, inifile:
collected 1 item
test_app.py F [100%]
================================================================================= FAILURES ==================================================================================
_________________________________________________________________________________ test_app __________________________________________________________________________________
def test_app():
app = create_app()
> app2 = create_app()
test_app.py:5:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
app.py:21: in create_app
rest_api.register_blueprint(_)
../flask-rest-api/flask_rest_api/__init__.py:81: in register_blueprint
blp.register_views_in_doc(self._app, self.spec)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <flask_rest_api.blueprint.Blueprint object at 0x103a3d470>, app = <Flask 'app'>, spec = <flask_rest_api.spec.APISpec object at 0x10433a400>
def register_views_in_doc(self, app, spec):
"""Register views information in documentation
If a schema in a parameter or a response appears in the spec
`definitions` section, it is replaced by a reference to its definition
in the parameter or response documentation:
"schema":{"$ref": "#/definitions/MySchema"}
"""
# This method uses the documentation information associated with the
# endpoint (in self._docs) to provide documentation for the route to
# the spec object.
for endpoint, doc in self._docs.items():
# doc is a dict of documentation per method for the endpoint
# {'get': documentation, 'post': documentation,...}
# Prepend Blueprint name to endpoint
endpoint = '.'.join((self.name, endpoint))
# Format operations documentation in OpenAPI structure
# Tag all operations with Blueprint name
# Merge manual doc
for key, (auto_doc, manual_doc) in doc.items():
self._prepare_doc(auto_doc, spec.openapi_version)
> auto_doc['tags'] = [self.name]
E TypeError: 'str' object does not support item assignment
../flask-rest-api/flask_rest_api/blueprint.py:161: TypeError
Hello!
First of all, thank you for such a great library!
I have and old legacy API that returns responses like so:
{
"type": "error",
"kind": "validation",
"message": { ... some message ...} # this is built upon exception text
}
# or
{
"type": "success",
"result": { ... some data ...} # this is what view returns
}
As you can see, the responses have envelope. Is it possible to implement such functionality in pythonic way still using @blp.response decorator?
Error condition can be determined on any exception (or specific like ValidationError to set kind and message fields) and on status method. Same for successful response.
Thanks!
Hello, i have defined two response types on my view function:
@blp.response(code=404, description="Product not found")
@blp.response(ProductSchema(), code=200)
def get(self, product_id):
...
Generated documentations contains description of those two possible response types and looks perfect.
If view is aborted via abort(404)
then everything works as expected, but i got an error otherwise:
2018-11-22 18:18:43.686 INFO 127.0.0.1 - - [22/Nov/2018 18:18:43] "GET /api/products/1/ HTTP/1.1" 500 -
Traceback (most recent call last):
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/app.py", line 2309, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/app.py", line 2295, in wsgi_app
response = self.handle_exception(e)
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/app.py", line 1741, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise
raise value
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise
raise value
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/views.py", line 88, in view
return self.dispatch_request(*args, **kwargs)
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/views.py", line 158, in dispatch_request
return meth(*args, **kwargs)
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask_rest_api/response.py", line 55, in wrapper
resp = jsonify(self._prepare_response_content(result_dump))
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/json/__init__.py", line 321, in jsonify
dumps(data, indent=indent, separators=separators) + '\n',
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/json/__init__.py", line 179, in dumps
rv = _json.dumps(obj, **kwargs)
File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py", line 238, in dumps
**kw).encode(obj)
File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 201, in encode
chunks = list(chunks)
File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 437, in _iterencode
o = _default(o)
File "/Users/asyncee/app/env/lib/python3.6/site-packages/flask/json/__init__.py", line 81, in default
return _json.JSONEncoder.default(self, o)
File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 180, in default
o.__class__.__name__)
TypeError: Object of type 'Response' is not JSON serializable
So my question is: am i doing everything correctly and multiple decorators supported?
Hi,
I feel it would be handy if we can use webargs dict with arguments decorator.
Or if we can use Marshmallow instance with some params for example:
@blp.arguments(Schema(only=["first", "second"]))
The reason is, we just need one Schema model for all arguments request. We can decide which params needed to be there, don't need all.
I know we can have load_only or dump_only in Marshmallow. But it would be more flexible if we can use instance with only=, partial=, exclude=...
I really like this library. Found it is the best fit for my projects.
If you are planing to support this in long run, I would like to help.
Thanks
So I was trying to do something akin to:
blp = Blueprint(
"users", "users", description="Operations on users"
)
@blp.route("/users")
class UsersResource(MethodView):
@blp.response(UserOutputSchema(many=True))
def index(self):
"""
Sum 1
Desc 1
"""
return User.query.all()
@blp.route("/users/<user_id>")
@blp.response(UserOutputSchema)
def get(self, user_id):
"""
Sum 2
Desc 2
"""
return User.query.get(user_id)
For this case the generated openapi spec for index is overridden with the summary and description values of get. As such:
...
"paths": {
"/users/{user_id}": {
"get": {
"summary": "Sum 2",
"description": "Desc 2",
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UserOutput"
}
}
}
}
},
"tags": [
"users"
],
"parameters": [
{
"in": "path",
"name": "user_id",
"required": true,
"schema": {
"type": "string"
}
}
]
}
},
"/users": {
"get": {
"summary": "Sum 2",
"description": "Desc 2",
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UserOutput"
}
}
}
}
},
"tags": [
"users"
]
}
}
},
...
What is the correct way to fix this behaviour, should both cases be separated in different classes?
In FlaskPlugin, add min/max to number converters.
Manage negative values introduced in Werkzeug 0.15 (pallets/werkzeug#1355).
So I was trying t implement a simple Patch use case but its proving to be a difficult affair. As Patch allows for partial submission of schema fields I tried the obvious solution and added the partial=True argument to my schema declaration.
@blp.arguments(UserInputSchema(partial=True))
This however leads to a problem related to the marshmallow model schema class, since it immediately tries to create a model instance from the passed object.
Am I missing something or does patch just not play well with the intended behavior of marshmallow's ModelSchema?
Thanks for the help in advance.
I can't see any way to add a description to a path parameter. Using the @blp.doc
decorator as in:
@blp.doc(parameters=[{"name": "thing_id", "description": "The ID of the thing"}])
just results in adding another parameter with a duplicate name and only a description. I have a local fix for this in FlaskPlugin
where I just scan the existing parameters and update rather than append if I find one with the same name. I can get this into a PR unless you have another way you'd rather approach this?
According to https://pypi.org/project/flask-rest-api/#files this library currently only provides wheels to PyPI. Best practice is to upload source distributions as well. Fixing should just be a matter of running python setup.py sdist upload
.
Thanks for maintaining flask-rest-api, hope to try it soon! (Would have tried it already if not for this issue, which makes it harder for me to use it on some internal infrastructure.)
In regular flask function I could return headers as last parameter:
return body, status_code, headers
That's how I return e.g. "Location" header.
In flask-rest-api I can't return such tuple nor Response object with custom headers because Api.response requires object to serialize.
I see that there is some headers handling in code https://github.com/Nobatek/flask-rest-api/blob/master/flask_rest_api/response.py#L74 but I do not know how to elegantly set them in my function.
If there's no elegant way then maybe handling return value as in flask (https://github.com/pallets/flask/blob/master/flask/app.py#L1890) should be implemented?
Currently I use flask_jwt_extended for API security, how can I define the need of passing Authorization header?
I tried using this:
@blp.doc(parameters={'Authorization': {'name': 'Authorization', 'in': 'header', 'description': 'Authorization: Bearer <access_token>', 'required': 'true'}})
but it throw an error
builtins.KeyError
KeyError: 'in
Hello!
I did not found standard way to describe a multipart request. Is it supported?
Currently i had to write something like this to let uploaded files work:
request_body_description = {
"content": {
"multipart/form-data": {
"schema": {
"type": "object",
"properties": {"logo": {"type": "string", "format": "binary"}},
}
}
}
}
@bp.route("/update-logo")
class UpdateLogo(MethodView):
@bp.response(code=204)
@bp.doc(requestBody=request_body_description)
def post(self):
file = flask.request.files["logo"]
filename = secure_filename(file.filename)
binary = file.read()
... do something with file ...
This code allows swagger to render input type="file" with name "logo" and send multipart request when executing request from web interface.
Am i missing something?
Thanks!
I'm looking for a way to add examples.
I'm trying to add them with Blueprint.doc
, but it seems they are thrown away by Blueprint._prepare_doc
.
Is there some other way?
Hi! I'm using your library as a convenience wrapper and I really like it!
However, one thing I've come across is that when I use the @blueprint.response
decorator on my view function, all responses are marshaled with the Schema and return a 200 response. The decorator does not respect when I've finished the request with an error code.
There are many times when I abort the request (using flask's abort
) instead of returning data. flask-rest-api
overrides my abort
s and return {errors}, 409
statements, so for any view function that returns an error, I cannot use @blueprint.response
. This means I'm using @blueprint.response
for some of my view functions and not for others, which makes for some very inconsistent and bug-prone code.
As far as I can think of, there isn't a reason you would ever want to marshal an error response to the schema, right? So would it be possible for flask-rest-api to only marshal the response with marshmallow if the view function returns "normal" data, but respect things with error codes, like flask's abort
? Maybe @blueprint.response
could include a keyword arg like respect_errors
which defaults to True or something like that?
Thanks again, great work!
The string that stops docstring parsing (---
) should be customizable.
According to the docs, "the part of the docstring following the `'---`` line is ignored."
This is normally great, but can cause issues when using some docstring styles such as NumPyDoc.
The separator value should be user-customizable via flask config value, perhaps OPENAPI_DOCSTRING_SEP
or something like that.
def get(...):
"""
Find pets by ID.
Return pets based on ID.
Parameters
----------
pet_id : int
The PK for the pet to query
Returns
-------
dict : :class:`orm.Pet`
A dict representation of the pet instance. When returned via flask, is
passed through :func:`flask.jsonify`.
"""
will be mangled to:
{
'get': {
'summary': 'Find pets by ID.',
'description': 'Return pets based on ID.\n\nParameters',
}
}
Note the "Parameters" string in the description.
app = Flask('My API')
app.config['OPENAPI_VERSION'] = '3.0.2'
app.config['OPENAPI_DOCSTRING_SEP'] = "Parameters"
api = Api(app)
def get(...):
"""
Find pets by ID.
Return pets based on ID.
Parameters
----------
pet_id : int
The PK for the pet to query
Returns
-------
dict : :class:`orm.Pet`
A dict representation of the pet instance. When returned via flask, is
passed through :func:`flask.jsonify`.
"""
{
'get': {
'summary': 'Find pets by ID.',
'description': 'Return pets based on ID.',
}
}
Any string should be acceptable as a separator. In addition, the None
value should also be accepted. If the separator is None
, then the entire docstring is included in the swagger details.
It looks like this docstring parsing is done in utils.load_info_from_docstring. I haven't investigated much further than that, but it looks like it might be pretty easy to implement.
I had an issue with URLs with trailing slash where flask would normally redirect.
e.g. @blp.route('/things/')
, a request for /things
should redirect to /things/
by default.
Example:
from flask import Flask
from flask_rest_api import Api, Blueprint
app = Flask(__name__)
app.config['OPENAPI_VERSION'] = '3.0.2'
api = Api(app)
blp = Blueprint('blp', __name__)
@blp.route('/things/')
def get_things():
return 'things'
api.register_blueprint(blp)
When calling the endpoint without the trailing slash you get a serialized 308 response with no Location header instead of a proper redirect response:
curl -i http://localhost:5001/things
HTTP/1.0 308 PERMANENT REDIRECT
Content-Type: application/json
Content-Length: 42
Server: Werkzeug/0.15.1 Python/3.6.7
Date: Tue, 26 Mar 2019 20:57:42 GMT
{"status":"308 Permanent Redirect: None"}
Is this expected?
I looked at the errorhandler code and saw it captures any type of HTTPException
, but the redirect triggers a werkzeug.routing.RequestRedirect
which is a subclass of HTTPException
and RoutingException
I worked around this in my app by overriding handle_http_exception
like this
from flask_rest_api import Api as BaseApi
from werkzeug.routing import RoutingException
class Api(BaseApi):
def handle_http_exception(self, error):
# Don't serialize redirects
if isinstance(error, RoutingException):
return error
return super().handle_http_exception(error)
But the framework probably should only register an error handler for codes above 400.
Cheers
This might sound silly, but the API's current name ('flask-rest-api') makes it practically impossible to Google.
Not only is it slightly frustrating when trying to recall the github or readthedocs URL in a bind, but in some possible future, it will make it all but impossible to try and find related posts on Stackoverflow or elsewhere.
I would strongly recommend picking a name (any name) that is somewhat unique and easy to search. I know it's going to be a bit of a mess updating, but probably still easier now, than another year down the road.
Hello.
Function flask_rest_api/utils.unpack_tuple_response
checks that result returned is a tuple and tries to unpack it.
This is not working if i'm returning tuple-like object, for example sqlalchemy result object (which can be a tuple subclass) or namedtuple. All this objects pass isinstance(rv, tuple)
.
First of all, thank you for this lib :)
I got the following marshmallow schema :
class Meta:
strict = True
ordered = True
a_date = fields.DateTime(
format='iso',
missing=datetime.combine(date.today(), time()))
But it fails while rendering the doc (json schema) with the error :
Object of type 'datetime' is not JSON serializable
It fails in In this file
json.dumps(self.spec.to_dict(), indent=2)
As a quick fix, I propose a simple datetime serializer:
def custom_date_serializer(obj):
"""Custom json serializer"""
if isinstance(obj, (datetime, date)):
return obj.isoformat()
raise TypeError ("Type %s not serializable" % type(obj))
json.dumps(self.spec.to_dict(), indent=2, default=custom_date_serializer)
I can't make my app have the Swagger route. This is the simplest app with PeeWee I came up with:
import marshmallow as ma
from flask import Flask
from flask.views import MethodView
from flask_rest_api import Api, Blueprint
from peewee import TextField
from playhouse.flask_utils import FlaskDB
class Config:
DATABASE = {
'name': 'database.db',
'engine': 'SqliteDatabase',
}
OPENAPI_URL_PREFIX = '/doc'
OPENAPI_REDOC_PATH = '/redoc'
OPENAPI_SWAGGER_UI_PATH = '/swagger'
OPENAPI_SWAGGER_URL = '/swagger'
API_SPEC_OPTIONS = {'x-internal-id': '2'}
class MyDB(FlaskDB):
def connect_db(self):
if self.database.is_closed():
super(MyDB, self).connect_db()
app = Flask('My API')
app.config.from_object(Config)
api = Api(app)
db = MyDB(app)
class Pet(db.Model):
name = TextField(unique=True)
db.database.create_tables([Pet])
pet = Pet(name='cici')
try:
pet.save()
except:
pass
# @api.definition('Pet')
class PetSchema(ma.Schema):
class Meta:
strict = True
ordered = True
id = ma.fields.Int(dump_only=True)
name = ma.fields.String()
blp = Blueprint(
'pets',
'pets',
url_prefix='/pets',
description='Operations on pets'
)
@blp.route('/')
class Pets(MethodView):
@blp.response(PetSchema(many=True))
def get(self):
"""List pets"""
return Pet.select()
api.register_blueprint(blp)
if __name__ == '__main__':
app.run()
These are the routes:
flask routes
Endpoint Methods Rule
---------------------- ------- -----------------------
api-docs.openapi_json GET /doc/openapi.json
api-docs.openapi_redoc GET /doc/redoc
pets.Pets GET /pets/
static GET /static/<path:filename>
How can I enable Swagger UI?
I tried using @blueprint.arguments(PetSchema, location='headers')
and I tried with location='headers'
in the schema field declaration without result. What is the proper way to use arguments in the headers? Thank you!
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.