from flask import Flaskfrom flask.ext.sqlalchemy import SQLAlchemyapp = Flask(__name__)app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'db = SQLAlchemy(app)class User(db.Model):id = db.Column(db.Integer, primary_key=True)username = db.Column(db.String(80), unique=True)email = db.Column(db.String(120), unique=True)def __init__(self, username, email):self.username = username
def __repr__(self):return '<User %r>' % self.username
from sqlalchemy import Column Integer, String, create_enginefrom sqlalchemy.orm import scoped_session, sessionmakerfrom sqlalchemy.ext.declarative import declarative_baseengine = create_engine('sqlite:////tmp/test.db', convert_unicode=True)db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))Base = declarative_base()Base.query = db_session.query_property()
class User(Base):__tablename__ = 'users'id = Column(Integer, primary_key=True)name = Column(String(50), unique=True)email = Column(String(120), unique=True)def __init__(self, name=None, email=None):self.name = nameself.email = emaildef __repr__(self):return '<User %r>' % (self.name)
I've been using SQLAlchemy with Flask via the Flask extension Flask-SQLAlchemy. Everything works great so far, but I foresee a potential problem once I start to use my database model outside of Flask. In the future I'd like to be able to use my models with non-Flask SQLAlchemy (a worker daemon process or with a PySide interface). "Well just use standard SQLAlchemy," you may say, " and fore-go the use of the extension". That was my first thought, but sadly some useful extensions (notably Flask-DebugToolbar) seem to like using the extension version and it is nice to be able to have Flask manage the database sessions in the way that it does. I'd like to not throw the baby out with the bath water.I realize that this is somewhat specific to Flask, but is there a way that I could do both? Can I create models with standard SQLAlchemy declarative and then somehow inject them into Flask-SQLAlchemy's way of doing things?
If it helps with the solution, I don't need to use any of the Model specific methods that Flask-SQLAlchemy provides (get_or_404, paginate, etc..) and I also specify a __tablename__ for all of my models, so I don't rely on Flask-SQLAlchemy generating that for me.I took a look at the source of Flask-SQLAlchemy (https://github.com/mitsuhiko/flask-sqlalchemy/blob/master/flask_sqlalchemy.py) and from what I can tell it seems that it's using Flask's signalling capabilities by customizing SQLAlchemy's session and mapper, but that is where my understanding ends (I'm still new to this whole stack, Python, Flask, SQLAlchemy) and I could use some pointers for how to proceed.
... but also this system precedes SQLAlchemy's 0.7 event model. Armin's immediate goal with flask-sqlalchemy is to migrate the extension to use the new event model, which would actually remove the need for the styles of registration I see here as the new system allows registration of event listeners on all sessions/mappers non-intrusively.
I was wondering if you could just clarify how I might convert a standard model object into a flask-sqlalchemy model object.
Using the two examples above, would I just take my User(Base) class and then assign it property out of the Flask db properties? Something like:User.session = db.session()User.engine = db.engine()I know those properties don't actually exist on User, but is that the kind of thing I should be looking to do? Move certain pieces into all of the models? or is there something higher level going on that will do this for me in some way? something else?
This seems like it would be a really good way to accomplish what I'm looking for and to move things forward as well. Once I read up on the requisite knowledge I may end up making an attempt at making this over the next little while. I had a drink with Armin last week and I'm not sure if his current stuff points him in this direction (of course you'd have to ask him for the real answer on that), but I certainly have a vested interest, so maybe I can do some of the grunt work.
- This is really the difficult one. In this case I think it just needs an attribute on the Flask app called sqlalchemy_queries which contains a tuple of queries. This is where I was thinking it may be better to assist with updating the plugin to just play nice with SQLAlchemy's default behaviours. Maybe I'm wrong?
The "User.session / User.engine" pattern suggests that a model class acts as a registry for how to get at a live database connection. That pattern is one that SQLAlchemy specifically discourages. In the Flask docs, you'll see that the "handle" to the state of a transaction is db.session. The objects like instances of User are then contained within that.
from flask import Flaskfrom flask_sqlalchemy import SQLAlchemyfrom sqlalchemy import Column, Integer, Stringfrom sqlalchemy.ext.declarative import declarative_base
app = Flask(__name__)app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'db = SQLAlchemy(app)
Base = declarative_base()
class User(Base):__tablename__ = 'users'id = Column(Integer, primary_key=True)name = Column(String(50), unique=True)email = Column(String(120), unique=True)def __init__(self, name=None, email=None):self.name = nameself.email = emaildef __repr__(self):return '<User %r>' % (self.name)
@app.before_first_requestdef setup():# Recreate database each time for demoBase.metadata.drop_all(bind=db.engine)Base.metadata.create_all(bind=db.engine)db.session.add(User('Bob Jones', 'b...@gmail.com'))db.session.add(User('Joe Quimby', 'e...@joes.com'))db.session.commit()@app.route('/')def root():users = db.session.query(User).all()return u"<br>".join([u"{0}: {1}".format(user.name, user.email) for user in users])if __name__ == '__main__':app.run('127.0.0.1', 5000)