Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Advanced : Nested Field" doc example is creating set() by mistake #513

Open
zarnovican opened this issue Oct 12, 2015 · 4 comments
Open
Labels

Comments

@zarnovican
Copy link

The second data structure is actually a valid syntax for set().

user_fields = {
    'id': fields.Integer,
    'name': fields.String,
}

user_list_fields = {
    fields.List(fields.Nested(user_fields)),
}

Looking at the code for marshal(), the function would never work with set. Bombs-out on https://github.com/flask-restful/flask-restful/blob/master/flask_restful/__init__.py#L632

I don't know how to correct the example since this snippet works for me (marshaling list of user objects):

user_fields = {
    'id':       fields.String(attribute='_id'),
    'email':    fields.String,
}

class User(Resource):

    @marshal_with(user_fields)
    def get(self, id):
        return model.get_user(id)

class UserList(Resource):

    @marshal_with(user_fields)
    def get(self):
        return model.users.values()     # returns a list of User() objects

So I guess, better example is needed where Nested with List is really required.

@joshfriend
Copy link
Member

Nice catch. It should really be something like:

user_list_fields = {
   'users':  fields.List(fields.Nested(user_fields)),
}

@zarnovican
Copy link
Author

It should really be something like..

I don't know. I've played with it for some time. Even with _fields as proper dictionary, marshaling looks weird. Full example:

from flask import Flask
from flask_restful import fields, marshal_with, Api, Resource

app = Flask(__name__)
api = Api(app)

class MyUser(object):

    def __init__(self, name):
        self.name = name

users = { 'joe': MyUser('joe'), 'jane': MyUser('jane'), }

user_fields = {
    'name':     fields.String,
}

user_list_fields = {
    'users':    fields.List(fields.Nested(user_fields)),
}

class User(Resource):

    @marshal_with(user_fields)
    def get(self, name):
        return users[name]

class UserList(Resource):

    @marshal_with(user_list_fields)
    def get(self):
        return users.values()

api.add_resource(UserList, '/users')
api.add_resource(User,     '/users/<name>', endpoint='user')

if __name__ == '__main__':
    app.run(debug=True)
$ curl localhost:5000/users/jane
{
    "name": "jane"
}
$ curl localhost:5000/users
{
    "users": null
}

My understanding is that marshal() will call itself on every element of the list (if returned data is a list). So it will try to marshal single User() object using user_list_fields.

@joshfriend
Copy link
Member

Try this instead?

class UserList(Resource):

    @marshal_with(user_list_fields)
    def get(self):
        return {'users': users.values()}

you would nest the users list inside another object to be able to also return other metadata info with the response, such as pagination info.

@zarnovican
Copy link
Author

Thanks! It worked. Though, I had to wrap it in list().

    @marshal_with(user_list_fields)
    def get(self):
        return { 'users': list(users.values()), }

Off-topic: It has to do with Py3's .values(). Code fails inside _get_value_for_key func because indexing on dict_values won't work.

>>> d = {'a': 1, 'b': 2, }
>>> vs = d.values()
>>> vs
dict_values([2, 1])
>>> is_indexable_but_not_string(vs)
True
>>> vs[1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'dict_values' object does not support indexing
>>> 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants