frol/flask-restplus-server-example

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 .

frol commented

.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.

frol commented

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
frol commented

Do you use Api / Namespace from flask_restplus_patched?

Yes , from flask_restplus_patched import Api, Resource, Namespace

Proof:
image

But the error was thrown from flask_restplus\api.py :

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

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:

  1. api_wrap.response is for Schema and ModelSchema from flask_restplus_patched or marshmallow or marshmallow_falsk .
  2. api_wrap.marshal_with is for api.model from flask_restplus .