1. Christoph Zwerschke
  2. sqlalchemy

Commits

Mike Bayer  committed d4afca1

illustrates a simple Query "hook" to implement query caching.

  • Participants
  • Parent commits bdb0772
  • Branches default

Comments (0)

Files changed (1)

File examples/query_caching/query_caching.py

View file
  • Ignore whitespace
+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()
+    
+    
+    
+