summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES4
-rw-r--r--examples/dynamic_dict/dynamic_dict.py83
2 files changed, 87 insertions, 0 deletions
diff --git a/CHANGES b/CHANGES
index 6c2de2878..8ec857dad 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(<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]
+