summaryrefslogtreecommitdiff
path: root/examples/custom_attributes
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2008-05-09 16:34:10 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2008-05-09 16:34:10 +0000
commit4a6afd469fad170868554bf28578849bf3dfd5dd (patch)
treeb396edc33d567ae19dd244e87137296450467725 /examples/custom_attributes
parent46b7c9dc57a38d5b9e44a4723dad2ad8ec57baca (diff)
downloadsqlalchemy-4a6afd469fad170868554bf28578849bf3dfd5dd.tar.gz
r4695 merged to trunk; trunk now becomes 0.5.
0.4 development continues at /sqlalchemy/branches/rel_0_4
Diffstat (limited to 'examples/custom_attributes')
-rw-r--r--examples/custom_attributes/custom_management.py190
1 files changed, 190 insertions, 0 deletions
diff --git a/examples/custom_attributes/custom_management.py b/examples/custom_attributes/custom_management.py
new file mode 100644
index 000000000..707e182ba
--- /dev/null
+++ b/examples/custom_attributes/custom_management.py
@@ -0,0 +1,190 @@
+"""this example illustrates how to replace SQLAlchemy's class descriptors with a user-defined system.
+
+This sort of thing is appropriate for integration with frameworks that redefine class behaviors
+in their own way, such that SQLA's default instrumentation is not compatible.
+
+The example illustrates redefinition of instrumentation at the class level as well as the collection
+level, and redefines the storage of the class to store state within "instance._goofy_dict" instead
+of "instance.__dict__". Note that the default collection implementations can be used
+with a custom attribute system as well.
+
+"""
+from sqlalchemy import *
+from sqlalchemy.orm import *
+
+from sqlalchemy.orm.attributes import set_attribute, get_attribute, del_attribute, is_instrumented
+from sqlalchemy.orm.collections import collection_adapter
+
+
+class MyClassState(InstrumentationManager):
+ def __init__(self, cls):
+ self.states = {}
+
+ def instrument_attribute(self, class_, key, attr):
+ pass
+
+ def install_descriptor(self, class_, key, attr):
+ pass
+
+ def uninstall_descriptor(self, class_, key, attr):
+ pass
+
+ def instrument_collection_class(self, class_, key, collection_class):
+ return MyCollection
+
+ def get_instance_dict(self, class_, instance):
+ return instance._goofy_dict
+
+ def initialize_instance_dict(self, class_, instance):
+ instance.__dict__['_goofy_dict'] = {}
+
+ def initialize_collection(self, key, state, factory):
+ data = factory()
+ return MyCollectionAdapter(key, state, data), data
+
+ def install_state(self, class_, instance, state):
+ self.states[id(instance)] = state
+
+ def state_getter(self, class_):
+ def find(instance):
+ return self.states[id(instance)]
+ return find
+
+class MyClass(object):
+ __sa_instrumentation_manager__ = MyClassState
+
+ def __init__(self, **kwargs):
+ for k in kwargs:
+ setattr(self, k, kwargs[k])
+
+ def __getattr__(self, key):
+ if is_instrumented(self, key):
+ return get_attribute(self, key)
+ else:
+ try:
+ return self._goofy_dict[key]
+ except KeyError:
+ raise AttributeError(key)
+
+ def __setattr__(self, key, value):
+ if is_instrumented(self, key):
+ set_attribute(self, key, value)
+ else:
+ self._goofy_dict[key] = value
+
+ def __delattr__(self, key):
+ if is_instrumented(self, key):
+ del_attribute(self, key)
+ else:
+ del self._goofy_dict[key]
+
+class MyCollectionAdapter(object):
+ """An wholly alternative instrumentation implementation."""
+ def __init__(self, key, state, collection):
+ self.key = key
+ self.state = state
+ self.collection = collection
+ setattr(collection, '_sa_adapter', self)
+
+ def unlink(self, data):
+ setattr(data, '_sa_adapter', None)
+
+ def adapt_like_to_iterable(self, obj):
+ return iter(obj)
+
+ def append_with_event(self, item, initiator=None):
+ self.collection.add(item, emit=initiator)
+
+ def append_without_event(self, item):
+ self.collection.add(item, emit=False)
+
+ def remove_with_event(self, item, initiator=None):
+ self.collection.remove(item, emit=initiator)
+
+ def remove_without_event(self, item):
+ self.collection.remove(item, emit=False)
+
+ def clear_with_event(self, initiator=None):
+ for item in list(self):
+ self.remove_with_event(item, initiator)
+ def clear_without_event(self):
+ for item in list(self):
+ self.remove_without_event(item)
+ def __iter__(self):
+ return iter(self.collection)
+
+ def fire_append_event(self, item, initiator=None):
+ if initiator is not False and item is not None:
+ self.state.get_impl(self.key).fire_append_event(self.state, item,
+ initiator)
+
+ def fire_remove_event(self, item, initiator=None):
+ if initiator is not False and item is not None:
+ self.state.get_impl(self.key).fire_remove_event(self.state, item,
+ initiator)
+
+ def fire_pre_remove_event(self, initiator=None):
+ self.state.get_impl(self.key).fire_pre_remove_event(self.state,
+ initiator)
+
+class MyCollection(object):
+ def __init__(self):
+ self.members = list()
+ def add(self, object, emit=None):
+ self.members.append(object)
+ collection_adapter(self).fire_append_event(object, emit)
+ def remove(self, object, emit=None):
+ collection_adapter(self).fire_pre_remove_event(object)
+ self.members.remove(object)
+ collection_adapter(self).fire_remove_event(object, emit)
+ def __getitem__(self, index):
+ return self.members[index]
+ def __iter__(self):
+ return iter(self.members)
+ def __len__(self):
+ return len(self.members)
+
+if __name__ == '__main__':
+ meta = MetaData(create_engine('sqlite://'))
+
+ table1 = Table('table1', meta, Column('id', Integer, primary_key=True), Column('name', Text))
+ table2 = Table('table2', meta, Column('id', Integer, primary_key=True), Column('name', Text), Column('t1id', Integer, ForeignKey('table1.id')))
+ meta.create_all()
+
+ class A(MyClass):
+ pass
+
+ class B(MyClass):
+ pass
+
+ mapper(A, table1, properties={
+ 'bs':relation(B)
+ })
+
+ mapper(B, table2)
+
+ a1 = A(name='a1', bs=[B(name='b1'), B(name='b2')])
+
+ assert a1.name == 'a1'
+ assert a1.bs[0].name == 'b1'
+ assert isinstance(a1.bs, MyCollection)
+
+ sess = create_session()
+ sess.save(a1)
+
+ sess.flush()
+ sess.clear()
+
+ a1 = sess.query(A).get(a1.id)
+
+ assert a1.name == 'a1'
+ assert a1.bs[0].name == 'b1'
+ assert isinstance(a1.bs, MyCollection)
+
+ a1.bs.remove(a1.bs[0])
+
+ sess.flush()
+ sess.clear()
+
+ a1 = sess.query(A).get(a1.id)
+ assert len(a1.bs) == 1