afaiknow these traits are like my own static_types, i.e. descriptors holding metadata and applying it to attribute access. i have been combining SA with static_type for more than 2 years now, since SA 3.0.
The approach i did in the beginning was to replace the object's __dict__ by something smart that is static_type-aware. When InstrumentionManager framework came into place, i did not find it any different, as SA still uses the __dict__ for data access. The difference is that now my __dict__ replacement is created once and not at every attr.access. i did suggest one patch about replaceing the SA's obj.__dict__.whatever usage with an explicit set of methods (so one knows what access to mime), and that was making SA 1-2% faster, but it wasn't accepted. basicaly now there's a thin layer on top of SA, then SA itself, then a thick layer underneath managing the data (the fake __dict__). declarative+traits... u'll end up where i was. dbcook.sf.net is doing that - since beginning. and it's switchable on/off. It all works well and stable, in project with 250-300 classes, although about 15% slower than without it (-:) The sa2static code: svn co http://dbcook.svn.sourceforge.net/svnroot/dbcook/trunk/dbcook/usage/static_type/ The static_type itself: svn co https://dbcook.svn.sourceforge.net/svnroot/dbcook/static_type whole dbcook: svn co https://dbcook.svn.sourceforge.net/svnroot/dbcook/trunk i have quite some experience fighting this field, ask if u want. ciao svilen www.svilendobrev.com On Sunday 08 February 2009 01:51:20 cputter wrote: > Hi guys and girls, > > I've recently discovered the joys of using sqlalchemy and would > love to using it together with Traits. A few months back there was > an attempt to integrate sqlalchemy into traits, though it wasn't > really comprehensive in exploiting all of sqlalchemy's potential. > > So I'm trying to work on that and combine ext.Declarative with > traits. The basic idea is to use the DeclarativeMeta type to > generate Columns from Traits and pass those on for the Declarative > extension to do its magic. This would allow mixing of sqlalchemy > attributes and trait attributes in a single class so that we could > still make use of all the relational setup sqlalchemy does in any > case. > > Reading through several threads and looking at Elixir's SA > integration helped me a bit though I couldn't find any > documentation on how to implement the InstrumentationManager > interface. I'm assuming this would be essential for letting Traits > and SQLAlchemy play well together. > > There's still a lot of work to do, and I'm not really sure what > needs to be done for everything to work properly. Would really > appreciate it if someone could help me out. > > > Here's an example of how it's working at the moment, I'll add the > actual implementation at the end. > > ##################################### > class User(HasTraitsORM): > __tablename__ = 'users' > > id = Column('id', Integer, primary_key=True) > name = Str(sqldb=True) > > def _name_changed(self, old, new): > print 'Changed name from %s to %s.' % (old, new) > > def __repr__(self): > return '<User(%s, %s)>' % (self.id, self.name) > > people = ['John', 'Charls','Steve','Smith','Jane'] > > for per in people: > obj = User(name=per) > sess = sqlservice.Session() > sess.add(obj) > sess.commit() > sess.close() > print obj > > session = sqlservice.Session() > print '\nQuery all users\n' > for user in session.query(User).order_by(User.name).all(): > print user > > session.close() > > > ############################ > > Which spits out: > ############################### > Changed name from to John. > <User(users.id, )> > Changed name from to Charls. > <User(users.id, )> > Changed name from to Steve. > <User(users.id, Steve)> > Changed name from to Smith. > <User(users.id, Smith)> > Changed name from to Jane. > <User(users.id, Jane)> > > Query all users > > <User(2, Charls)> > <User(1, John)> > > ############################## > > Which is really strange behaviour. There's obviously something > wrong in my implementation of HasTraitsORM but why the different > results within the same loop??? Why add only two instances? > > Totally baffles me. > > Here's the rest of my code, hope somehow can help me out. It's > very messy, I've been hacking at it like crazy with no success :-) > > Hope you're all having a great weekend. > -Chris > > > ############################## > > # Standard library imports. > import logging > > # Enthought library imports > from enthought.preferences.api import Preferences > from enthought.traits.api import \ > HasTraits, MetaHasTraits, Int, Str, Bool, Float, Any,\ > String, Enum, Python, \ > on_trait_change, TraitListObject > > # Package imports > import sqlalchemy > from sqlalchemy import Column, Integer > from sqlalchemy.schema import MetaData > from sqlalchemy.orm.interfaces import MapperProperty, > InstrumentationManager > from sqlalchemy.orm.attributes import get_attribute, set_attribute, > is_instrumented > from sqlalchemy.orm.collections import InstrumentedList, > collection_adapter > from sqlalchemy.ext.declarative import _as_declarative > > > # Setup a logger for this module. > logger = logging.getLogger(__name__) > > > TRAIT_MAPPING = { > Int : 'sqlalchemy.Integer', > Str : 'sqlalchemy.Text', > Enum : 'sqlalchemy.Text', > String : 'sqlalchemy.Text', > Float : 'sqlalchemy.Float', > Bool : 'sqlalchemy.Boolean', > } > > > class HasTraitsORMState(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 ObjectCollection > > def get_instance_dict(self, class_, instance): > return instance.__dict__ > > def initialize_instance_dict(self, class_, instance): > instance.reset_traits() > > def initialize_collection(self, key, state, factory): > data = factory() > return ObjectCollectionAdapter(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 ObjectCollectionAdapter(object): > """ > An adapter for SQLAlchemy for TraitsListObject which is the > collection > we use for storing instances of classes within attributes > of other > classes. > > TODO: Think of a way to get this to behave like a normal > traited > attribute of objects. > At the moment collection attributes are set through the > mapper by SA > when using relations or backref. > > """ > 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.append(item, emit=initiator) > > def append_without_event(self, item): > self.collection.append(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 ObjectCollection(TraitListObject): > __emulates__ = list > > def __init__( self, trait, object, name, value ): > pass > > def __init__(self): > self.members = list() > def append(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) > def __repr__(self): > if len(self.members) == 0: > return '[empty collection]' > s = '[' > for item in self.members[0:max(5, len(self.members))]: > s += '%s,' % item > s += '...]' > return s > > > class DeclarativeMetaTraits(MetaHasTraits): > pass > > # def __new__(cls, classname, bases, dict_): > # return MetaHasTraits.__new__(cls, classname, bases, dict_) > > def __init__(cls, classname, bases, dict_): > > if '_decl_class_registry' in cls.__dict__: > super(DeclarativeMetaTraits, cls).__init__(cls, > classname, bases, dict_) > return > > if '__tablename__' not in dict_: > setattr(cls, '__tablename__', cls.__name__) > > # create sql columns from flagged traits > if '__class_traits__' in cls.__dict__: > traits = cls.__dict__['__class_traits__'] > for key, trait in traits.iteritems(): > if getattr( trait, 'sqldb' ): > args = getattr( trait, 'col_args' ) or () > kwargs = getattr( trait, 'col_kwargs' ) or {} > if 'name' not in kwargs: > kwargs['name'] = key > if 'type_' not in kwargs: > kwargs['type_'] = eval(TRAIT_MAPPING[type > (trait.trait_type)]) > > if getattr(trait, 'sqlpk'): > kwargs['primary_key'] = True > > c = Column(*args, **kwargs) > dict_[key] = c > setattr(cls, key, c) > > # Pass this along to SA's declarative mapper stuff > _as_declarative(cls, classname, dict_) > > super(DeclarativeMetaTraits, cls).__init__(cls, classname, > bases, dict_) > > > class HasTraitsORM(HasTraits): > __metaclass__ = DeclarativeMetaTraits > > # some SA hacking > __sa_instrumentation_manager__ = HasTraitsORMState > > _decl_class_registry = dict() > metadata = MetaData() > > # Any implicit traits added by SQLAlchemy are transient and > should not be > # copied through .clone_traits(), copy.copy(), or pickling. > _ = Python(transient=True) > > > # For some reason we need to override these, our superclass > screws things up > > def __getattr__(self, key): > if is_instrumented(self, key): > return get_attribute(self, key) > else: > try: > return getattr(self, key) > except: > pass > > def __setattr__(self, key, value): > if is_instrumented(self, key): > set_attribute(self, key, value) > else: > setattr(self, key, value) > > > ################################################# > > > --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To post to this group, send email to sqlalchemy@googlegroups.com To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en -~----------~----~----~----~------~----~------~--~---