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

Error when jsonify query result #47

Closed
jonahfang opened this issue Sep 1, 2017 · 15 comments
Closed

Error when jsonify query result #47

jonahfang opened this issue Sep 1, 2017 · 15 comments

Comments

@jonahfang
Copy link

jonahfang commented Sep 1, 2017

code:

from sanic import response
...
    u1 = await User.create(nickname='fantixd')
    u2 = await User.get(u1.id)

    return response.json(u2)

Error:

  File "/usr/local/lib/python3.6/site-packages/sanic/response.py", line 246, in json
    return HTTPResponse(json_dumps(body, **kwargs), headers=headers,
OverflowError: Maximum recursion level reached

@fantix
Copy link
Member

fantix commented Sep 1, 2017

This is because ujson does not support serializing a SQLAlchemy clause element:

In [1]: import sqlalchemy, ujson

In [2]: ujson.dumps(sqlalchemy.select([123]))
---------------------------------------------------------------------------
OverflowError                             Traceback (most recent call last)
<ipython-input-2-2ef38474e0b5> in <module>()
----> 1 ujson.dumps(sqlalchemy.select([123]))

OverflowError: Maximum recursion level reached

And u2.query is exactly one of such. To bypass this defect, use a custom JSON encoder. See also sanic-org/sanic#905

@jonahfang
Copy link
Author

jonahfang commented Sep 1, 2017

I wrote my jsonify method as:


def jsonify_gino_model(rec):
    if type(rec) == list:
        return [jsonify_gino_model(r) for r in rec]

    if hasattr(rec,'__table__'):
        columns = rec.__table__.columns
        d = {}
        for c in columns:
            fld = str(c).split('.')[1]
            d[fld] = getattr(rec,fld)
    else:
        #rec is plain obj
        d = rec

    return d

@fantix
Copy link
Member

fantix commented Sep 1, 2017

Add a Model.to_dict() method might be helpful?

@jonahfang
Copy link
Author

How abount Model.jsonify() ?

@fantix
Copy link
Member

fantix commented Sep 1, 2017

Nah, that would normally be a application-specific behavior, and GINO cannot jsonify all types of columns in a way that is acceptable for all scenarios, DateTime for example.

@jonahfang
Copy link
Author

You are right , Model.to_dict() is good!

@fantix
Copy link
Member

fantix commented Sep 1, 2017

to_dict it is then. 😈

@fantix fantix closed this as completed in 529a53c Sep 1, 2017
qdzzyb2014 pushed a commit to qdzzyb2014/gino that referenced this issue Sep 1, 2017
@qdzzyb2014
Copy link
Collaborator

qdzzyb2014 commented Sep 1, 2017

how about implement __iter__@fantix

@jonahfang
Copy link
Author

jonahfang commented Sep 1, 2017

My new jsonify_model method changed to:

def jsonify_model(rec):
    if type(rec) == list:
        return [jsonify_model(r) for r in rec]

    if hasattr(rec,'__table__'):
        columns = rec.__table__.columns
        d = {}
        for c in columns:
            fld = str(c).split('.')[1]
            v = getattr(rec,fld)
            if type(v) == datetime.datetime:
                d[fld] = str(v)
            else:
                d[fld] = v
    elif type(rec) == asyncpg.Record:
        ds = rec.items()
        d = {}
        for vd in ds:
            if type(vd[1]) == datetime.datetime:
                d[vd[0]] = str(vd[1])   #otherwise return time int
            else:
                d[vd[0]] = vd[1]
    else:
        #rec is plain object
        d = rec

    return d

It seems that asyncpg.Record can get data by .items() (it implements iter?)

@fantix
Copy link
Member

fantix commented Sep 1, 2017

@qdzzyb2014 the thing is, Model should be a generic base class for user-customized models. We'll never know how user may iterate their model objects for. So defining a default __iter__ is not a good idea IMO.

@fantix
Copy link
Member

fantix commented Sep 2, 2017

@jonahfang asyncpg.Record is both a tuple and a dict at the same time, it has __iter__ as tuple and items as dict. However as stated previously, I'd prefer Model stay as a plain Python object, leaving the "mixin" features for users - one can always mix in a dict interface or an iterable interface, through the declarative_base parameters. As for your jsonify, I'd suggest this:

    if isinstance(rec, db.Model):
        d = {}
        for k, v in rec.to_dict().items():
            if isinstance(v, datetime.datetime):
                d[fld] = v.strftime('YOUR_FORMAT_HERE')
            else:
                d[fld] = v

@jonahfang
Copy link
Author

jonahfang commented Sep 5, 2017

The following code is a bit ugly:

if isinstance(rec, db.Model):
...

I have to got 'db' first?

@fantix
Copy link
Member

fantix commented Sep 5, 2017

Well hasattr(rec, 'to_dict') works too, but db is usually a global variable as MetaData is.

@jonahfang
Copy link
Author

jonahfang commented Sep 5, 2017

Can I write as:

import sqlalchemy as sa
...
if isinstance(rec,sa.MetaData):

@fantix
Copy link
Member

fantix commented Sep 5, 2017

No you cannot.

fantix added a commit to decentfox/gino that referenced this issue Jan 29, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants