Assume we are building a web application that depends on SQLAlchemy. Typical web applications are structured by models, controllers and templates (views). Most of operations that depend on models are finished in controllers.
def list_posts(): session = Session() posts = session.query(Post).order_by(Post.created_at.desc()).all() return render_template('list_posts.html', posts=posts)
Codes like this pattern cause a blank screen when the browser are loading it. Then, the result list are rendered suddenly at once.
To avoid the blank screen, we can make queries lazy:
def list_posts(): session = Session() posts = session.query(Post).order_by(Post.created_at.desc()) return render_template('list_posts.html', posts=posts)
See the difference between both second lines of the former example code and the current. The first example calls the all() method of the query object, but the second does not. SQLAlchemy’s query objects don’t execute itself and fetch its result until it has realized (for example, looped under for statement, or applied to list() or tuple()), and the only purpose of the all() method is just to realize the query. In short, query objects are implicitly executed when they are really needed.
So, execution and fetching the result set of the posts query happen while the template is being rendered. As a result, the browser renders whole result list slightly slowly but gradually. This lazy approach helps hasty users to feel that they are waiting less, but total elapsed time still doesn’t become shorter.
def list_posts(): session = Session() posts = session.query(Post).order_by(Post.created_at.desc()).promise() return render_template('list_posts.html', posts=posts)
When the promise() method is called, it creates an another thread for the future. And the execution of the query has started running in the created underneath thread. The posts object, the return value which is an instance of Future, is similar to the lazy result set object explained above. It is not a real result set physically, but the promised result set. If iteration over posts, promised result set, has tried, it fetches immediately the real result set of the execution which is run from the underneath thread, or if the result set isn’t prepared, it waits for the underneath thread to finish its query execution (that may be some prepared already).
As result, similarly to the lazy approach of the above, the browser renders result list gradually, but unlike to the lazy approach, relatively fast.
import future from sqlalchemy.orm.session import sessionmaker Session = sessionmaker(query_cls=future.Query)
Or you can make future.Query the query class of a session instance optionally:
session = Session(query_cls=future.Query)
How to promise a future query is not hard. Just call the promise() method:
posts = session.query(Post).promise()
assert isinstance(posts, future.Future)
Then, iterate this future query (posts in the example) when you really need it:
for post in posts: print post.title, "by", post.author
If the posts finished the execution in the another thread, it fetches the result set immediately. If its execution hasn’t finished, the another thread joins the main thread and it has blocked until its execution has finished.
You can make this the default query class of your session:
from sqlalchemy.orm import sessionmaker import future Session = sessionmaker(query_cls=future.Query)
Promoised future query result.
Returns the results promised as a list. This blocks the underlying execution thread until the execution has finished if it is not yet.
|Returns:||the results promised|
The source code can be found from the GitHub project page and checked out via Git.
$ git clone git://github.com/lunant/SQLAlchemy-Future.git