'SQLAlchemy Falcon Class error

I am trying to set up an API using Falcon and SQLAlchemy using a postgresql database. Using gunicorn to start the API

I am getting the following error when trying to access /v1/users.

I tried to browse the SQLAlchemy documentation but could not find any working solution.

Thanks

[2017-03-15 10:20:26 +0100] [20516] [ERROR] Error handling request /v1/users
    Traceback (most recent call last):
      File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/gunicorn/workers/sync.py", line 135, in handle
        self.handle_request(listener, req, client, addr)
      File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/gunicorn/workers/sync.py", line 176, in handle_request
        respiter = self.wsgi(environ, resp.start_response)
      File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/falcon/api.py", line 209, in __call__
        responder(req, resp, **params)
      File "/Users/juliencourtes/Documents/projects/FalconAPI/app/api/v1/users.py", line 50, in on_get
        users = session.query(User).all()
      File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/sqlalchemy/orm/scoping.py", line 157, in do
        return getattr(self.registry(), name)(*args, **kwargs)
      File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 1330, in query
        return self._query_cls(entities, self, **kwargs)
      File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 139, in __init__
        self._set_entities(entities)
      File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 148, in _set_entities
        entity_wrapper(self, ent)
      File "/Users/juliencourtes/Documents/projects/FalconAPI/venv/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 3947, in __init__
        "expected - got '%r'" % (column, )
    sqlalchemy.exc.InvalidRequestError: SQL expression, column, or mapped entity expected - got '<function User at 0x1040216a8>'

My projects files

main.py

import falcon
from app.api.v1 import users
from app.middleware import DatabaseSessionManager
from app.database import db_session, init_session
from app.api.common import base

class App(falcon.API):

    def __init__(self,*args, **kwargs):
        super(App, self).__init__(*args, **kwargs)
        self.add_route('/',base.BaseResource())
        self.add_route('/v1/users',users.Collection())


init_session()
mdlw = [DatabaseSessionManager(db_session)]
application = App(middleware=mdlw)

session.py

import sqlalchemy.orm.scoping as scoping
from sqlalchemy.exc import SQLAlchemyError

from app import config

class DatabaseSessionManager(object):
    def __init__(self, db_session):
        self._session_factory = db_session
        self._scoped = isinstance(db_session, scoping.ScopedSession)

    def process_request(self, req, res, resource=None):
        req.context['session'] = self._session_factory


    def process_response(self, req, res, resource=None):
        session = req.context['session']

        if config.DB_AUTOCOMMIT:
            try:
                session.commit()
            except SQLAlchemyError as ex:
                session.rollback()


        if self._scoped:
            session.remove()
        else:
            session.close()

database init.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
import psycopg2
from app import config


db_session = scoped_session(sessionmaker())
engine = create_engine('postgresql+psycopg2://xxxxx@localhost/falcon_api')


def init_session():
    db_session.configure(bind=engine)

user.py

from sqlalchemy import Column
from sqlalchemy import String, Integer,Text
from sqlalchemy.dialects.postgresql import UUID, JSONB
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

def User(Base):
    __tablename__ = 'user'

    id = Column('id', UUID, primary_key=True)
    firstname = Column('firstname', String)
    lastname = Column('lastname', String)

    #It tells python how to print the class, used for debugging
    def __repr__(self):
        return "<User(id='%s', name='%s', lastname='%s')>"% \
            (self.id, self.firstname, self.lastname)

    def __init__(self, id , firstname, lastname):
        self.id = id
        self.firstname = firstname
        self.lastname = lastname

api/v1/users.py

import falcon
import json
from sqlalchemy.ext.declarative import declarative_base
try:
    from collections import OrderedDict
except ImportError:
    OrderedDict = dict
from app.models import User
class Collection():
    """
    Handle for endpoint: /v1/users
    """

    def to_json(self, body_dict):
        return json.dumps(body_dict)


    def on_error(self, resp, error=None):
        resp.status = error['status']
        meta = OrderedDict()
        meta['code'] = error['code']
        meta['message'] = error['message']

        obj = OrderedDict()
        obj['meta'] = meta
        resp.body = self.to_json(obj)

    def on_success(self, resp, data=None):
        resp.status = falcon.HTTP_200
        meta = OrderedDict()
        meta['code'] = 200
        meta['message'] = 'OK'

        obj = OrderedDict()
        obj['meta'] = meta
        obj['data'] = data
        resp.body = self.to_json(obj)


    def on_get(self, req, resp):
        session = req.context['session']

        #Bugging here
        users = session.query(User).all()


Solution 1:[1]

I have faced same problem..It got fixed in my case for the following changes.. instead session.remove() use the following code.

if self._scoped:
     self._session_factory.remove()

This will work.

Solution 2:[2]

We create models for SQL in python using Class which inherits built in class from ORM like SQLAlchemy. Here you are trying to create User model. But as it need to be a class to work properly instead you declared it as a function in user.py.

All you have to do to make it work is just change def User to class User.

So user.py will look something like:

...
class User(Base):
    __tablename__ = 'user'
...

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 suhail areekkan
Solution 2 Jake Peralta