Hi All,
I am using Flask-SQLAlchemy on a legacy database. My SQLA classes / tables are setup using declarative base and autoload. In addition to using the class to access persisted data, I also use plain old Python to enrich them with ephemeral data.
Every time I load a User object, I'd like the instance for that particular object / row to retain its plain old python instance data.
My understanding is that Flask-SQLAlchemy will by default use a separate scoped_session for each request. And that means at the end of the request session and user will go out of scope. So in the next request to /users/Bob will instantiate a new User object whose will be reinitialized . . . each time I make this request I will be told it was "asked 1 time".
Alternately, if I call /users_alt/Bob, then second time I call a DetachedInstanceError will be raised when I attempt to read user.owners.
I suspect there's a pattern used to accomplish what I want to do, but I have not tried the right Google search. Any recommendations on how to approach this or searches to try / doc sections to read? The state I'm storing in times_name_read_since_launch is actually complex enough that I don't want to persist it and have to keep that dependent state synchronized.
Alternately is there a way for me to reattach a SQLA object to the current requests' session? Then, ideally, I could get updates from the database to User or Owner objects, but retain my ephemeral state in the plain old python.
Thanks for the help,
Rob
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import MetaData
from sqlalchemy import orm
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = connection_string
db = SQLAlchemy(app)
class Owner(db.Model):
__table__ = db.Table('Owners', MetaData(), autoload=True, autoload_with=db.engine)
class User(db.Model):
__table__ = db.Table('users', MetaData(), autoload=True, autoload_with=db.engine)
owners = db.relationship('Owner', primaryjoin="foreign(User.userId)==remote(Owner.userId)", uselist=True,
backref=db.backref('user', uselist=False))
users_seen = dict()
@orm.reconstructor
def init_on_load(self):
self.times_name_read_since_launch = 0
@classmethod
def lookup_by_name(cls, name):
user_ = User.users_seen.get(name)
if user_ is None:
user_ = User.query.filter(User.UserName == name).one()
return user_
@classmethod
def store_by_name(cls, user_):
if user_.UserName not in User.users_seen:
User.users_seen[user_.UserName] = user_
@property
def name(self):
self.times_name_read_since_launch += 1
return self.UserName
@app.route("/users/<string:name>")
def user(name):
user_ = User.query.filter(User.UserName == name).one()
return "{} with {} - asked {} times".format(user_.name, user_.owners, user_.times_name_read_since_launch)
@app.route("/users_alt/<string:name>")
def user_alt(name):
user_ = User.lookup_by_name(name)
User.store_by_name(user_)
owners = None
if user_.times_name_read_since_launch > 0:
# don't lazy load addresses the first request, simulates more complex actual behavior desired
owners = user_.owners
return "{} with {} - asked {} times".format(user_.name, owners, user_.times_name_read_since_launch)
db.create_all()