I'm wondering what the best way to setup a database session when the
database url is configured in an application factory. Because the session
in being created in a factory, the views aren't able to access the session
on import time. My current solution is to have a method called setup_db()
which configures the database, and db_session() which the views call to
return the session. I'm wondering if there is a better way to do this?
Maybe by attaching the session to the application somehow? Or maybe
dynamically calling db_session() each time is okay in this scenario? I
looked through many of the examples and couldn't find anything.
Note: I'm using a bunch of models from a different (non-Flask) project which
inherit from a separate declarative_base class, so the SQLAlchemy example on
the Flask site won't work for me.
Any help is appreciated!
Here is the abridged code:
/app
app.py
config.py
database.py
models.py (simlinked in from another project)
/frontend
frontend.py
====app.py ====
from frontend.frontend import frontend
from database import setup_db
def app_factory(env='development'):
app = Flask(__name__)
app.config.from_object(config)
app.register_module(frontend, url_prefix='/')
setup_db(app)
return app
app = app_factory()
if __name__ == '__main__':
app.run()
==== frontend.py =====
from database import db_session
from models import User
frontend = Module(__name__)
@frontend.after_request
def remove_session(response):
db_session().remove()
return response
...
==== database.py =====
__engine = None
__db_session = None
def db_session():
return __db_session
def setup_db(app):
global __engine
global __db_session
__engine = create_engine(app.config['DATABASE'],
echo=app.config['DEBUG'])
__db_session = scoped_session(sessionmaker(bind=__engine))
return db_session
==== models.py ====
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
name = Column(String)
...
One way I've done it is to make the db available by attaching it to the
global (g) object.
E.g. do this in your app.py, where db is your session returned from
setup_db. Also, remove the global instances in your database.py (__engine
and __db_session).
@request_started.connect_via(app)
def before_request(sender):
g.db = db
@app.after_request
def shutdown_session(response):
g.db.remove()
return response
Then, you can access the db in your views with g.db.
A couple things to note: use the request_started.connect_via decorator IFF
you are using signals. You can instead use @app.before_request, which is
normally fine/suggested; I don't use it because other signal-based injectors
that need access to the db won't get it due to the order when before_request
is called.
-Tom
On Fri, Mar 11, 2011 at 3:35 PM, Kenshi Kawaguchi <kenshi@recurse.ca> wrote:
> I'm wondering what the best way to setup a database session when the
> database url is configured in an application factory. Because the session
> in being created in a factory, the views aren't able to access the session
> on import time. My current solution is to have a method called setup_db()
> which configures the database, and db_session() which the views call to
> return the session. I'm wondering if there is a better way to do this?
> Maybe by attaching the session to the application somehow? Or maybe
> dynamically calling db_session() each time is okay in this scenario? I
> looked through many of the examples and couldn't find anything.
>
> Note: I'm using a bunch of models from a different (non-Flask) project
> which inherit from a separate declarative_base class, so the SQLAlchemy
> example on the Flask site won't work for me.
>
> Any help is appreciated!
>
> Here is the abridged code:
>
> /app
> app.py
> config.py
> database.py
> models.py (simlinked in from another project)
> /frontend
> frontend.py
>
> ====app.py ====
> from frontend.frontend import frontend
> from database import setup_db
>
> def app_factory(env='development'):
> app = Flask(__name__)
> app.config.from_object(config)
> app.register_module(frontend, url_prefix='/')
> setup_db(app)
>
> return app
>
> app = app_factory()
>
> if __name__ == '__main__':
> app.run()
>
> ==== frontend.py =====
> from database import db_session
> from models import User
>
> frontend = Module(__name__)
>
> @frontend.after_request
> def remove_session(response):
> db_session().remove()
> return response
> ...
>
> ==== database.py =====
> __engine = None
> __db_session = None
>
> def db_session():
> return __db_session
>
> def setup_db(app):
> global __engine
> global __db_session
>
> __engine = create_engine(app.config['DATABASE'],
> echo=app.config['DEBUG'])
> __db_session = scoped_session(sessionmaker(bind=__engine))
> return db_session
>
> ==== models.py ====
> Base = declarative_base()
> class User(Base):
> __tablename__ = 'users'
> name = Column(String)
> ...
>
Ahh thanks for the pointers. So am I right in saying that I don't need a scoped_session() because the global g object is context-local? Is there any harm in using a scoped_session anyway? On Sat, Mar 12, 2011 at 5:32 AM, Tom Zellman <tzellman@forthought.net>wrote: > One way I've done it is to make the db available by attaching it to the > global (g) object. > > E.g. do this in your app.py, where db is your session returned from > setup_db. Also, remove the global instances in your database.py (__engine > and __db_session). > > @request_started.connect_via(app) > def before_request(sender): > g.db = db > > @app.after_request > def shutdown_session(response): > g.db.remove() > return response > > Then, you can access the db in your views with g.db. > > A couple things to note: use the request_started.connect_via decorator IFF > you are using signals. You can instead use @app.before_request, which is > normally fine/suggested; I don't use it because other signal-based injectors > that need access to the db won't get it due to the order when before_request > is called. > > -Tom > > > On Fri, Mar 11, 2011 at 3:35 PM, Kenshi Kawaguchi <kenshi@recurse.ca>wrote: > >> I'm wondering what the best way to setup a database session when the >> database url is configured in an application factory. Because the session >> in being created in a factory, the views aren't able to access the session >> on import time. My current solution is to have a method called setup_db() >> which configures the database, and db_session() which the views call to >> return the session. I'm wondering if there is a better way to do this? >> Maybe by attaching the session to the application somehow? Or maybe >> dynamically calling db_session() each time is okay in this scenario? I >> looked through many of the examples and couldn't find anything. >> >> Note: I'm using a bunch of models from a different (non-Flask) project >> which inherit from a separate declarative_base class, so the SQLAlchemy >> example on the Flask site won't work for me. >> >> Any help is appreciated! >> >> Here is the abridged code: >> >> /app >> app.py >> config.py >> database.py >> models.py (simlinked in from another project) >> /frontend >> frontend.py >> >> ====app.py ==== >> from frontend.frontend import frontend >> from database import setup_db >> >> def app_factory(env='development'): >> app = Flask(__name__) >> app.config.from_object(config) >> app.register_module(frontend, url_prefix='/') >> setup_db(app) >> >> return app >> >> app = app_factory() >> >> if __name__ == '__main__': >> app.run() >> >> ==== frontend.py ===== >> from database import db_session >> from models import User >> >> frontend = Module(__name__) >> >> @frontend.after_request >> def remove_session(response): >> db_session().remove() >> return response >> ... >> >> ==== database.py ===== >> __engine = None >> __db_session = None >> >> def db_session(): >> return __db_session >> >> def setup_db(app): >> global __engine >> global __db_session >> >> __engine = create_engine(app.config['DATABASE'], >> echo=app.config['DEBUG']) >> __db_session = scoped_session(sessionmaker(bind=__engine)) >> return db_session >> >> ==== models.py ==== >> Base = declarative_base() >> class User(Base): >> __tablename__ = 'users' >> name = Column(String) >> ... >> > >