2

I'm new to Flask and SQLAlchemy and I have spent so much time trying to get my database query to present as JSON using a Flask url (not flask_restful).

I thought I had it working by using n0nSmoker/SQLAlchemy-serializer. It worked using a simple query using this syntax selecting my Trade SQLAlchemy class:

trades = Trade.query.all()

I was able to return trades.to_dict() with no problem.

However because I need to do a group_by, I need to use the more flexible syntax of db.session.query(Trade, Trade.exchange, Trade.trader....... etc this method stopped working.

I have looked at other options like the inspection system but it seems a bit beyond my skill level. Plus it seems like some of the methods like _asdict() aren't available at the row level.

I'm a bit lost and not sure how to proceed. Any help would be hugely appreciated.

Edit: More code requested: top of app.py

from flask import Flask
from flask_restful import Resource, Api
from models import *
from flask_sqlalchemy import SQLAlchemy
from sqlathanor import FlaskBaseModel, initialize_flask_sqlathanor

app = Flask(__name__)
app.config.from_pyfile('config.py')

db = SQLAlchemy(model_class = FlaskBaseModel)
db = initialize_flask_sqlathanor(db)

class Trades(Resource):
    def get(self):
        return AllTrades()

api.add_resource(Trades,'/')


if __name__ == '__main__':
    app.run(host="0.0.0.0")

from models.py

from app import db

class Trade(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    exchange = db.Column(db.String(255))
    trader = db.Column(db.String(255))
    symbol = db.Column(db.String(255))
    coin = db.Column(db.String(255))
    order_id = db.Column(db.String(255))
    price = db.Column(db.String(255))
    qty = db.Column(db.String(255))
    fee = db.Column(db.String(255))
    fee_currency = db.Column(db.String(255))
    order_time = db.Column(db.DateTime(timezone=False))
    side = db.Column(db.String(255))
    maker_taker = db.Column(db.String(255))


def AllTrades():
    #t = Trade
    # trades = db.session.query(
    #   t,
    #   t.exchange,
    #   t.trader,
    #   t.symbol,
    #   t.coin,
    #   db.func.sum(t.qty),
    #   db.func.sum(t.fee).label("fee"),
    #   t.fee_currency,
    #   t.order_id,
    #   t.order_time,
    #   t.maker_taker
    # ).group_by(t.order_id).all()

    trades = Trade.query.all()
    trades = trades.to_json()

    return trades

Full stack trace

    Traceback (most recent call last):
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask/app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask/app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask/_compat.py", line 32, in reraise
    raise value.with_traceback(tb)
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask/_compat.py", line 32, in reraise
    raise value.with_traceback(tb)
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask_restful/__init__.py", line 480, in wrapper
    resp = resource(*args, **kwargs)
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask/views.py", line 84, in view
    return self.dispatch_request(*args, **kwargs)
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask_restful/__init__.py", line 595, in dispatch_request
    resp = meth(*args, **kwargs)
  File "/Users/foo/Sites/Reporting_dev/app.py", line 18, in get
    return AllTrades()
  File "/Users/foo/Sites/Reporting_dev/models.py", line 42, in AllTrades
    trades = Trade.query.all()
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 514, in __get__
    return type.query_class(mapper, session=self.sa.session())
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/sqlalchemy/orm/scoping.py", line 74, in __call__
    return self.registry()
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/sqlalchemy/util/_collections.py", line 1001, in __call__
    return self.registry.setdefault(key, self.createfunc())
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 2939, in __call__
    return self.class_(**local_kw)
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 143, in __init__
    bind = options.pop('bind', None) or db.engine
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 877, in engine
    return self.get_engine()
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 887, in get_engine
    state = get_state(app)
  File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 570, in get_state
    'The sqlalchemy extension was not registered to the current ' \
AssertionError: The sqlalchemy extension was not registered to the current application.  Please make sure to call init_app() first.
Samuurai
  • 375
  • 2
  • 13
  • 1
    I recently read [this](https://stackoverflow.com/questions/5022066/how-to-serialize-sqlalchemy-result-to-json/51469201#51469201) answer to another similar question. I haven’t used the library but thought I’d bring it to your attention as sounds like it might help. – SuperShoot Jul 23 '18 at 12:32
  • 1
    There’s plenty of discussion about result serialisation in that question aside from the answer I linked to as well. – SuperShoot Jul 23 '18 at 12:33
  • 1
    I use [Flask-Marshmallow](https://flask-marshmallow.readthedocs.io/en/latest/) for this and it works really nicely. Here's a simple project where I've used it - https://github.com/tim-thompson/bonsai-trees – Tim Thompson Jul 23 '18 at 17:34

1 Answers1

2

I’d suggest you take a look at a new library I released a week or two ago called SQLAthanor.

It adds simple serialization support to SQLAlchemy models, and if you are also using Flask-SQLAlchemy, it works seamlessly with it as well.

Basically, the way it works is this:

  1. You define your Trade model the way you’re (presumably) already doing. The only difference is you import declarative_base or your base model from SQLAthanor instead of from SQLAlchemy (or Flask-SQLAlchemy).

  2. You configure your serialization rules when defining your model. This basically lets you say “when I serialize a Trade to JSON, include attributes X, Y, and Z but not P, D, and Q”.

  3. Execute your query however you would normally. You’ll get back a list of Trade instances, presumably. You can now serialize each instance to JSON by calling <instance>.to_json().

It’s worth mentioning that SQLAthanor:

  • supports serialization and de-serialization to/from JSON, CSV, YAML, and Python dict
  • supports serialization/de-serialization of Column attributes, relationships, hybrid properties, association proxies and regular Python @properties
  • supports custom pre/post processing functions for serialization/de-serialization at the attribute level (for validation or type coercion)
  • supports enabling/disabling serialization and de-serialization at the attribute level for particular formats (for example, accept passwords inbound, but never include them outbound)

It might be worth taking a look. Here’s a link to the (I hope comprehensive) documentation: http://sqlathanor.readthedocs.io/en/latest/

Hope this helps, and if you have questions or need help getting it working, just let me know (or post questions to SO with the tag sqlathanor)

Chris Modzelewski
  • 1,351
  • 10
  • 10
  • This looks really good and thanks also to @SuperShoot for pointing me in this direction. It looks like a great extension and the docs are well written, however I am not able to get it working. I see this error: File "......./Reporting_dev/app.py", line 9, in from sqlathanor import FlaskBaseModel, initialize_sqlathanor ImportError: cannot import name 'initialize_sqlathanor' – Samuurai Jul 26 '18 at 09:44
  • 1
    Ack! Looks like a typo in the docs. The function you want to import/use is `initialize_flask_sqlathanor` rather than `initialize_sqlathanor`. Sorry for the confusion - I’ll fix the docs in the next patch release (probably later today). – Chris Modzelewski Jul 26 '18 at 11:56
  • 1
    No worries! Best of luck, and if you run into trouble, please just let me know. – Chris Modzelewski Jul 26 '18 at 15:48
  • Hi again! Only just managed to get back to this. I've got a tiny bit further. I'm now getting this error: `File "/Users/foo/anaconda3/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 570, in get_state 'The sqlalchemy extension was not registered to the current ' \ AssertionError: The sqlalchemy extension was not registered to the current application. Please make sure to call init_app() first`. - any ideas on this one? Thanks – Samuurai Aug 09 '18 at 16:00
  • 1
    Can you post a more complete stack trace? Also, it would be helpful if you posted the code you're using to both initialize Flask-SQLAlchemy, and initialize SQLAthanor.That'll help us figure out if the issue is occurring where you are initializing SQLAthanor, or if it's elsewhere in your application (for example, where Flask-SQLAlchemy is getting initialized). – Chris Modzelewski Aug 09 '18 at 16:29
  • Thanks - I added my code. I think the problem could be because i'm using flask_restful...? – Samuurai Aug 10 '18 at 09:05
  • Also I removed flask_restful and it still says it's not registered to the current application. – Samuurai Aug 10 '18 at 14:00
  • 1
    Gotcha. I believe the issue has to do with how you are initializing `db` in the first place. With the initialization pattern you're using, you need to use: ``db = SQLAlchemy(app, model_class = FlaskBaseModel)``. This will register Flask-SQLAlchemy with your Flask application. For more information, take a look at: http://flask-sqlalchemy.pocoo.org/2.3/quickstart/ – Chris Modzelewski Aug 10 '18 at 14:21
  • 1
    Also, with today's (significant) new release, I updated SQLAthanor's docs to show the more complete pattern you're using. – Chris Modzelewski Aug 10 '18 at 16:27