From 30f55a50165e56d79dfc5f720616d4d9cc400b33 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Wed, 7 May 2008 14:48:04 +0000 Subject: [PATCH] - added an example dynamic_dict/dynamic_dict.py, illustrating a simple way to place dictionary behavior on top of a dynamic_loader. --- CHANGES | 4 ++ examples/dynamic_dict/dynamic_dict.py | 83 +++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 examples/dynamic_dict/dynamic_dict.py diff --git a/CHANGES b/CHANGES index 6c2de28789..8ec857dadd 100644 --- a/CHANGES +++ b/CHANGES @@ -69,6 +69,10 @@ CHANGES their operands and only operate on sets, frozensets or subclasses of the collection type. Previously, they would accept any duck-typed set. + + - added an example dynamic_dict/dynamic_dict.py, illustrating + a simple way to place dictionary behavior on top of + a dynamic_loader. - sql - Added COLLATE support via the .collate() diff --git a/examples/dynamic_dict/dynamic_dict.py b/examples/dynamic_dict/dynamic_dict.py new file mode 100644 index 0000000000..682def78c3 --- /dev/null +++ b/examples/dynamic_dict/dynamic_dict.py @@ -0,0 +1,83 @@ +"""Illustrates how to place a dictionary-like facade on top of a dynamic_loader, so +that dictionary operations (assuming simple string keys) can operate upon a large +collection without loading the full collection at once. + +This is something that may eventually be added as a feature to dynamic_loader() itself. + +Similar approaches could be taken towards sets and dictionaries with non-string keys +although the hash policy of the members would need to be distilled into a filter() criterion. + +""" + +class MyProxyDict(object): + def __init__(self, parent, collection_name, keyname): + self.parent = parent + self.collection_name = collection_name + self.keyname = keyname + + def collection(self): + return getattr(self.parent, self.collection_name) + collection = property(collection) + + def keys(self): + # this can be improved to not query all columns + return [getattr(x, self.keyname) for x in self.collection.all()] + + def __getitem__(self, key): + x = self.collection.filter_by(**{self.keyname:key}).first() + if x: + return x + else: + raise KeyError(key) + + def __setitem__(self, key, value): + try: + existing = self[key] + self.collection.remove(existing) + except KeyError: + pass + self.collection.append(value) + +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy import * +from sqlalchemy.orm import * + +Base = declarative_base(engine=create_engine('sqlite://')) + +class MyParent(Base): + __tablename__ = 'parent' + id = Column(Integer, primary_key=True) + name = Column(String(50)) + _collection = dynamic_loader("MyChild", cascade="all, delete-orphan") + + def child_map(self): + return MyProxyDict(self, '_collection', 'key') + child_map = property(child_map) + +class MyChild(Base): + __tablename__ = 'child' + id = Column(Integer, primary_key=True) + key = Column(String(50)) + parent_id = Column(Integer, ForeignKey('parent.id')) + + +Base.metadata.create_all() + +sess = create_session(autoflush=True, transactional=True) + +p1 = MyParent(name='p1') +sess.save(p1) + +p1.child_map['k1'] = k1 = MyChild(key='k1') +p1.child_map['k2'] = k2 = MyChild(key='k2') + + +assert p1.child_map.keys() == ['k1', 'k2'] + +assert p1.child_map['k1'] is k1 + +p1.child_map['k2'] = k2b = MyChild(key='k2') +assert p1.child_map['k2'] is k2b + +assert sess.query(MyChild).all() == [k1, k2b] + -- 2.47.3