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 [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---