From: Mike Bayer Date: Sun, 10 Jan 2010 18:29:48 +0000 (+0000) Subject: this definitely has to go - theres bugs in merge(), serialization which X-Git-Tag: rel_0_5_8~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5556a4a678a4fabda6055050505b2f5c4e419260;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git this definitely has to go - theres bugs in merge(), serialization which prevent it from being useful. will have a much more comprehensive example in 0.6. --- diff --git a/examples/query_caching/per_relation.py b/examples/query_caching/per_relation.py deleted file mode 100644 index 6e0df46f64..0000000000 --- a/examples/query_caching/per_relation.py +++ /dev/null @@ -1,176 +0,0 @@ -""" -Ready for some really powerful stuff ? - -We're going to use Beaker caching, and create functions that load and cache what we want, which -can be used in any scenario. Then we're going to associate them with many-to-one relations -for individual queries. - -Think of it as lazy loading from a long term cache. For rarely-mutated objects, this is a super -performing way to go. - -""" - -from sqlalchemy.orm.query import Query, _generative -from sqlalchemy.orm.interfaces import MapperOption -from sqlalchemy.orm.session import Session -from sqlalchemy.sql import visitors - -class CachingQuery(Query): - """override __iter__ to pull results from a callable - that might have been attached to the Query. - - """ - def __iter__(self): - if hasattr(self, 'cache_callable'): - try: - ret = self.cache_callable(self) - except KeyError: - ret = list(Query.__iter__(self)) - for x in ret: - self.session.expunge(x) - - return iter(self.session.merge(x, dont_load=True) for x in ret) - - else: - return Query.__iter__(self) - -class FromCallable(MapperOption): - """A MapperOption that associates a callable with particular 'path' load. - - When a lazyload occurs, the Query has a "path" which is a tuple of - (mapper, key, mapper, key) indicating the path along relations from - the original mapper to the endpoint mapper. - - """ - - propagate_to_loaders = True - - def __init__(self, key): - self.cls_ = key.property.parent.class_ - self.propname = key.property.key - - def __call__(self, q): - raise NotImplementedError() - - def process_query(self, query): - if query._current_path: - mapper, key = query._current_path[-2:] - if mapper.class_ is self.cls_ and key == self.propname: - query.cache_callable = self - -def params_from_query(query): - """Pull the bind parameter values from a query. - - This takes into account any scalar attribute bindparam set up. - - E.g. params_from_query(query.filter(Cls.foo==5).filter(Cls.bar==7))) - would return [5, 7]. - - - """ - - v = [] - def visit_bindparam(bind): - value = query._params.get(bind.key, bind.value) - v.append(value) - visitors.traverse(query._criterion, {}, {'bindparam':visit_bindparam}) - return v - -if __name__ == '__main__': - """Usage example. We'll use Beaker to set up a region and then a short def - that loads a 'Widget' object by id. - - """ - from sqlalchemy.orm import sessionmaker, scoped_session - from sqlalchemy import create_engine - - # beaker 1.4 or above - from beaker.cache import CacheManager - - # sample CacheManager. In reality, use memcached. (seriously, don't bother - # with any other backend.) - cache_manager = CacheManager( - cache_regions={ - 'default_region':{'type':'memory','expire':3600} - } - ) - - # SQLA configuration - engine=create_engine('sqlite://', echo=True) - Session = scoped_session(sessionmaker(query_cls=CachingQuery, bind=engine)) - - from sqlalchemy import Column, Integer, String, ForeignKey - from sqlalchemy.orm import relation - from sqlalchemy.ext.declarative import declarative_base - - # mappings - Base = declarative_base() - - class User(Base): - __tablename__ = 'user' - id = Column(Integer, primary_key=True) - name = Column(String(100)) - widget_id = Column(Integer, ForeignKey('widget.id')) - - widget = relation("Widget") - - class Widget(Base): - __tablename__ = 'widget' - id = Column(Integer, primary_key=True) - name = Column(String(100)) - - # Widget loading. - - @cache_manager.region('default_region', 'byid') - def load_widget(widget_id): - """Load a widget by id, caching the result in Beaker.""" - - return Session.query(Widget).filter(Widget.id==widget_id).first() - - class CachedWidget(FromCallable): - """A MapperOption that will pull user widget links from Beaker. - - We build a subclass of FromCallable with a __call__ method - so that the option itself is pickleable. - - """ - def __call__(self, q): - return [load_widget(*params_from_query(q))] - - Base.metadata.create_all(engine) - - sess = Session() - - # create data. - w1 = Widget(name='w1') - w2 = Widget(name='w2') - sess.add_all( - [User(name='u1', widget=w1), User(name='u2', widget=w1), User(name='u3', widget=w2)] - ) - sess.commit() - - # call load_widget with 1 and 2. this will cache those widget objects in beaker. - w1 = load_widget(1) - w2 = load_widget(2) - - # clear session entirely. - sess.expunge_all() - - # load users, sending over our option. - u1, u2, u3 = sess.query(User).options(CachedWidget(User.widget)).order_by(User.id).all() - - print "------------------------" - - # access the "Widget". No SQL occurs below this line ! - assert u1.widget.name == 'w1' - - # access the same "Widget" on u2. Local w1 is reused, no extra cache roundtrip ! - assert u2.widget.name == 'w1' - assert u2.widget is u1.widget - - assert u3.widget.name == 'w2' - - # user + the option (embedded in its state) - # are pickleable themselves (important for further caching) - import pickle - assert pickle.dumps(u1) \ No newline at end of file