From: Mike Bayer Date: Mon, 2 Jun 2008 15:27:38 +0000 (+0000) Subject: illustrates a simple Query "hook" to implement query caching. X-Git-Tag: rel_0_5beta1~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c998539b40fc22149d637bd4224215f6b81914f4;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git illustrates a simple Query "hook" to implement query caching. --- diff --git a/examples/query_caching/query_caching.py b/examples/query_caching/query_caching.py new file mode 100644 index 0000000000..1ac8230b6c --- /dev/null +++ b/examples/query_caching/query_caching.py @@ -0,0 +1,87 @@ +from sqlalchemy.orm.query import Query, _generative +from sqlalchemy.orm.session import Session + +# the cache. This would be replaced with the caching mechanism of +# choice, i.e. LRU cache, memcached, etc. +_cache = {} + +class CachingQuery(Query): + + # generative method to set a "cache" key. The method of "keying" the cache + # here can be made more sophisticated, such as caching based on the query._criterion. + @_generative() + def with_cache_key(self, cachekey): + self.cachekey = cachekey + + # override the _clone() method. a future release + # will just fix _clone() in Query to not hardcode the class so this won't be needed. + def _clone(self): + q = CachingQuery.__new__(CachingQuery) + q.__dict__ = self.__dict__.copy() + return q + + # single point of object loading is __iter__(). objects in the cache are not associated + # with a session and are never returned directly; only merged copies. + def __iter__(self): + if hasattr(self, 'cachekey'): + try: + ret = _cache[self.cachekey] + except KeyError: + ret = list(Query.__iter__(self)) + for x in ret: + self.session.expunge(x) + _cache[self.cachekey] = ret + + return iter(self.session.merge(x, dont_load=True) for x in ret) + + else: + return Query.__iter__(self) + +# currently the easiest way to get a custom Query class in the mix is just +# to subclass Session. A decorated sessionmaker() would probably work too. +class CacheableSession(Session): + def __init__(self, **kwargs): + super(CacheableSession, self).__init__(**kwargs) + self._query_cls = CachingQuery + + +# example usage +if __name__ == '__main__': + from sqlalchemy import Column, create_engine, Integer, String + from sqlalchemy.orm import sessionmaker + from sqlalchemy.ext.declarative import declarative_base + + Session = sessionmaker(class_=CacheableSession) + + Base = declarative_base(engine=create_engine('sqlite://', echo=True)) + + class User(Base): + __tablename__ = 'users' + id = Column(Integer, primary_key=True) + name = Column(String(100)) + + def __repr__(self): + return "User(name=%r)" % self.name + + Base.metadata.create_all() + + sess = Session() + + sess.add_all( + [User(name='u1'), User(name='u2'), User(name='u3')] + ) + sess.commit() + + # cache two user objects + sess.query(User).with_cache_key('u2andu3').filter(User.name.in_(['u2', 'u3'])).all() + + sess.close() + + sess = Session() + + # pull straight from cache + print sess.query(User).with_cache_key('u2andu3').all() + + + +