summaryrefslogtreecommitdiff
path: root/examples/query_caching/query_caching.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2008-06-02 15:27:38 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2008-06-02 15:27:38 +0000
commitc998539b40fc22149d637bd4224215f6b81914f4 (patch)
tree7e78f30f679d9dbfd4e6322a86f36d0bbe57d62d /examples/query_caching/query_caching.py
parente525aee01556e59ff9fc02dd68fd6a38532fe45a (diff)
downloadsqlalchemy-c998539b40fc22149d637bd4224215f6b81914f4.tar.gz
illustrates a simple Query "hook" to implement query caching.
Diffstat (limited to 'examples/query_caching/query_caching.py')
-rw-r--r--examples/query_caching/query_caching.py87
1 files changed, 87 insertions, 0 deletions
diff --git a/examples/query_caching/query_caching.py b/examples/query_caching/query_caching.py
new file mode 100644
index 000000000..1ac8230b6
--- /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()
+
+
+
+