diff options
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | examples/dynamic_dict/dynamic_dict.py | 83 |
2 files changed, 87 insertions, 0 deletions
@@ -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(<collation>) diff --git a/examples/dynamic_dict/dynamic_dict.py b/examples/dynamic_dict/dynamic_dict.py new file mode 100644 index 000000000..682def78c --- /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]
+
|