diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2008-06-02 15:27:38 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2008-06-02 15:27:38 +0000 |
| commit | c998539b40fc22149d637bd4224215f6b81914f4 (patch) | |
| tree | 7e78f30f679d9dbfd4e6322a86f36d0bbe57d62d /examples/query_caching/query_caching.py | |
| parent | e525aee01556e59ff9fc02dd68fd6a38532fe45a (diff) | |
| download | sqlalchemy-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.py | 87 |
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() + + + + |
