How do I marshal pagination data ?
eromoe opened this issue · 8 comments
Hello, thank you for this great project !
I have a problem with marshmallow, which the example doesn't cover.
My schema :
from flask_restplus_patched import ModelSchema
from src.extensions import ma
# # ma init at app startup
# from flask_marshmallow import Marshmallow
# ma = Marshmallow()
class CorpusSchema(ModelSchema):
id = fields.Int(dump_only=True)
tags = fields.Nested(TagSchema, many=True, exclude=('hexcolor', 'shortcut',), dump_only=True)
doc_count = fields.Int(dump_only=True)
class Meta:
model = Corpus
exclude = ('documents',)
dateformat = '%Y-%m-%d %H:%M:%S'
class PaginationSchema(ma.Schema):
total = fields.Integer()
has_next = fields.Boolean()
has_prev = fields.Boolean()
page = fields.Integer()
page_size = fields.Integer()
class CorpusPaginationSchema(PaginationSchema):
corpusList = fields.Nested(CorpusSchema, many=True, dump_only=True)
It is usually need return data with pagination . Here, as you see, CorpusPaginationSchema
have many CorpusSchema
, CorpusSchema
have many TagSchema
. I think this may be the problem.
I wrote view as below:
@api_wrap.route('/corpus')
class CorpusListPage(Resource):
@api_wrap.doc('get_corpus_list')
@api_wrap.marshal_with(CorpusPaginationSchema())
def get(self):
page, page_size = get_pagination_info()
result = Corpus.query.paginate(page, page_size, error_out=False)
return {
'total': result.total,
'has_next': result.has_next,
'has_prev': result.has_prev,
'corpusList': corpuses_schema.dump(result.items).data
}
But got error :
018-04-26 10:55:41,732 ERROR: Exception on /api/corpus [GET] [in D:\Anaconda3\envs\py3\lib\site-pac
ages\flask\app.py:1560]
raceback (most recent call last):
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\api.py", line 313, in wrapper
resp = resource(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\resource.py", line 44, in dispatch_re
uest
resp = meth(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\marshalling.py", line 110, in wrapper
return marshal(resp, self.fields, self.envelope, mask)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\marshalling.py", line 54, in marshal
for k, v in fields.items())
ttributeError: 'CorpusPaginationSchema' object has no attribute 'items'
I am very confused how to use marshal_with
correctly . Hope there would be a good example/tip for my case .
.marshal_with()
expects Flask-RESTplus schemas (i.e. not Marshmallow schemas). Use .response()
decorator instead:
@api.response(CorpusPaginationSchema())
It works, thank you!
But swagger throw error :
Traceback (most recent call last):
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\api.py", line 313, in wrapper
resp = resource(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\resource.py", line 44, in dispatch_re
quest
resp = meth(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\api.py", line 771, in get
return self.api.__schema__
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\api.py", line 209, in __getattr__
raise AttributeError('Api does not have {0} attribute'.format(name))
AttributeError: Api does not have __schema__ attribute
My api is blow:
api = Blueprint('api', __name__, url_prefix='/api')
api_wrap = Api(api, version='1.0', title='Annotator API',
description='Annotator API',
)
I also tried to use Namespace
corpus_api = Namespace('corpus')
api_wrap.add_namespace(corpus_api)
But got similar error :
ERROR in app [D:\Anaconda3\envs\py3\lib\site-packages\flask\app.py:1560]:
Exception on /api/swagger.json [GET]
--------------------------------------------------------------------------------
Traceback (most recent call last):
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\api.py", line 207, in __getattr__
return getattr(self.default_namespace, name)
AttributeError: 'Namespace' object has no attribute '__schema__'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\api.py", line 313, in wrapper
resp = resource(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\resource.py", line 44, in dispatch_re
quest
resp = meth(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\api.py", line 771, in get
return self.api.__schema__
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\api.py", line 209, in __getattr__
raise AttributeError('Api does not have {0} attribute'.format(name))
AttributeError: Api does not have __schema__ attribute
2018-04-26 17:29:52,132 ERROR: Exception on /api/swagger.json [GET] [in D:\Anaconda3\envs\py3\lib\si
te-packages\flask\app.py:1560]
Traceback (most recent call last):
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\api.py", line 207, in __getattr__
return getattr(self.default_namespace, name)
AttributeError: 'Namespace' object has no attribute '__schema__'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\api.py", line 313, in wrapper
resp = resource(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\resource.py", line 44, in dispatch_re
quest
resp = meth(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\api.py", line 771, in get
return self.api.__schema__
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\api.py", line 209, in __getattr__
raise AttributeError('Api does not have {0} attribute'.format(name))
AttributeError: Api does not have __schema__ attribute
Then I afraid this error was caused by using ma.Schema
.
So I remove above code , and wrote a view as
class CorpusSchema(ModelSchema):
id = fields.Int(dump_only=True)
#tags = fields.Nested(TagSchema, many=True, exclude=('hexcolor', 'shortcut',), dump_only=True)
doc_count = fields.Int(dump_only=True)
class Meta:
model = Corpus
exclude = ('documents',)
dateformat = '%Y-%m-%d %H:%M:%S'
@api_wrap.route('/corpus/<int:id>')
@api_wrap.doc(responses={404: 'Corpus not found'}, params={'id': 'The Corpus ID'})
class CorpusPage(Resource):
@api_wrap.doc('get_corpus')
@api_wrap.marshal_with(CorpusSchema())
def get(self, id):
c = Corpus.query.get_or_404(id)
return c
This time got error :
2018-04-26 17:31:53,120 ERROR: Exception on /api/swagger.json [GET] [in D:\Anaconda3\envs\py3\lib\si
te-packages\flask\app.py:1560]
Traceback (most recent call last):
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\api.py", line 313, in wrapper
resp = resource(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask\views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\resource.py", line 44, in dispatch_re
quest
resp = meth(*args, **kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\api.py", line 771, in get
return self.api.__schema__
File "D:\Anaconda3\envs\py3\lib\site-packages\werkzeug\utils.py", line 73, in __get__
value = self.func(obj)
File "G:\Project\allproject_git\basin-text-extractor\antor\flask_restplus_patched\api.py", line 15
, in __schema__
return Swagger(self).as_dict()
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\swagger.py", line 168, in as_dict
paths[extract_path(url)] = self.serialize_resource(ns, resource, url, kwargs)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\swagger.py", line 307, in serialize_r
esource
path[method] = self.serialize_operation(doc, method)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\swagger.py", line 313, in serialize_o
peration
'responses': self.responses_for(doc, method) or None,
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\swagger.py", line 398, in responses_f
or
responses[code]['schema'] = self.serialize_schema(model)
File "D:\Anaconda3\envs\py3\lib\site-packages\flask_restplus\swagger.py", line 449, in serialize_s
chema
raise ValueError('Model {0} not registered'.format(model))
ValueError: Model <CorpusSchema(many=False, strict=False)> not registered
127.0.0.1 - - [26/Apr/2018 17:31:53] "GET /api/swagger.json HTTP/1.1" 500 -
This is strange.
Try extending your class PaginationSchema
from flask_restplus_patched.Schema
instead of ma.Schema
Same error AttributeError: Api does not have __schema__ attribute
.
And ModelSchema
has different error ValueError: Model <CorpusSchema(many=False, strict=False)> not registered
detail in previous comment.
env:
- python 3.6
- flask_restplus_patched latest
Do you use Api
/ Namespace
from flask_restplus_patched
?
Yes , from flask_restplus_patched import Api, Resource, Namespace
Seems it was due to environment .
I installed all dependencies in latest version at first. Just now, I created a fresh virtualenv, and installed flask-restplus-server-example/app/requirements.txt
. Then, error Api does not have __schema__ attribute
gone.
But I still got error :
raise ValueError('Model {0} not registered'.format(model))
ValueError: Model <CorpusSchema(many=False, strict=False)> not registered
I do use ModelSchema
in flask_restplus_patched
here .
Then discover that I misunderstood ModelSchema
from flask_restplus_patched
. It is same as Marshmallow schemas
, not flask_restplus schemas
.
So conclusion:
api_wrap.response
is forSchema
andModelSchema
fromflask_restplus_patched
ormarshmallow
ormarshmallow_falsk
.api_wrap.marshal_with
is forapi.model
fromflask_restplus
.