Question

We're using Flask-Restful for implementing an API. As database we use MongoDB and MongoEngine as ODM. To get MongoEngine to work with Restful, we followed this blog article. For getting the correct json-format we using the builtin marsheling-methods. This works perfectly for single objects (e.g. one item of a collection), but when marsheling a list of objects (e.g. all items of a collection), an AttributeError is raised (although we use the same syntax as for single objects). This is how our model and our views look like (I don't paste the routes, as they are in a separate file and work).

model:

class Task(db.Document):
    name = db.StringField()
    description_mini = db.StringField()

views:

parser = reqparse.RequestParser()
parser.add_argument('task_id', type=str)

task_format = {
    "name": fields.String,
    "description_mini": fields.String
}

class TasksView(Resource):

    @marshal_with(task_format)
    def get(self):
        tasks = Task.objects().all()
        return tasks, 200


class TaskDetailView(Resource):

    @marshal_with(task_format)
    def get(self):
        args = parser.parse_args()
        startup_id = args['task_id']

        task = Task.objects(id=task_id).first()

        return task, 200

full stacktrace:

AttributeError

Traceback (most recent call last)
File "/project/venv/lib/python2.7/site-packages/flask/app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "/project/venv/lib/python2.7/site-packages/flask/app.py", line 1820, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/project/venv/lib/python2.7/site-packages/flask_restful/__init__.py", line 257, in error_router
return self.handle_error(e)
File "/project/venv/lib/python2.7/site-packages/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/project/venv/lib/python2.7/site-packages/flask/app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/project/venv/lib/python2.7/site-packages/flask_restful/__init__.py", line 257, in error_router
return self.handle_error(e)
File "/project/venv/lib/python2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "/project/venv/lib/python2.7/site-packages/flask/app.py", line 1461, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/project/venv/lib/python2.7/site-packages/flask_restful/__init__.py", line 397, in wrapper
resp = resource(*args, **kwargs)
File "/project/venv/lib/python2.7/site-packages/flask/views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "/project/venv/lib/python2.7/site-packages/flask_restful/__init__.py", line 487, in dispatch_request
resp = meth(*args, **kwargs)
File "/project/venv/lib/python2.7/site-packages/flask_restful/__init__.py", line 562, in wrapper
return marshal(data, self.fields), code, headers
File "/project/venv/lib/python2.7/site-packages/flask_restful/__init__.py", line 533, in marshal
return OrderedDict(items)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/collections.py", line 52, in __init__
self.__update(*args, **kwds)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_abcoll.py", line 547, in update
for key, value in other:
File "/project/venv/lib/python2.7/site-packages/flask_restful/__init__.py", line 532, in <genexpr>
for k, v in fields.items())
File "/project/venv/lib/python2.7/site-packages/flask_restful/fields.py", line 104, in output
value = get_value(key if self.attribute is None else self.attribute, obj)
File "/project/venv/lib/python2.7/site-packages/flask_restful/fields.py", line 37, in get_value
return _get_value_for_keys(key.split('.'), obj, default)
File "/project/venv/lib/python2.7/site-packages/flask_restful/fields.py", line 42, in _get_value_for_keys
return _get_value_for_key(keys[0], obj, default)
File "/project/venv/lib/python2.7/site-packages/flask_restful/fields.py", line 51, in _get_value_for_key
return obj[key]
File "/project/venv/lib/python2.7/site-packages/mongoengine/queryset/base.py", line 152, in __getitem__
raise AttributeError
AttributeError
Was it helpful?

Solution

When you want to marshal a list you have to define the fields as a list as well.

I think this will work:

task_list_format = {
    'tasks': fields.List(fields.Nested(task_format))
}

class TasksView(Resource):

    @marshal_with(task_list_format)
    def get(self):
        tasks = Task.objects().all()
        return { 'tasks': tasks }, 200

I believe it is not possible to return a plain list using the marshaling support in Flask-RESTful, it always expects a dictionary. For that reason I put the list under a "tasks" key.

I hope this helps.

OTHER TIPS

From what I understand, the problem is that mongoengine's Queryset object lazily queries the database and that Flask-restful/restplus marshalling expects a list.

I could make it work with

task_format = {
    "name": fields.String,
    "description_mini": fields.String
}

class TasksView(Resource):

    @marshal_with(task_format)
    def get(self):
        tasks = Task.objects().all()
        return list(tasks)

try flask_restful.marshal_with_fields:

>>> from flask_restful import marshal_with_field, fields
>>> @marshal_with_field(fields.List(fields.Integer))
... def get():
...     return ['1', 2, 3.0]
...
>>> get()
[1, 2, 3]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top