On Sep 6, 2012, at 6:05 PM, Jacob Biesinger wrote: > Hi all, > > Are there any best practices/caveats for overloading attribute access with > declarative sqlalchemy? I feel like the checks in setattr and getattr are > very hacky and are occasionally giving me strange surprises... > > from sqlalchemy import Integer, String, Column, create_engine, PickleType > from sqlalchemy.orm import Session > from sqlalchemy.ext.declarative import declarative_base > > > Base = declarative_base() > > class Foo(Base): > __tablename__ = 'foo' > id = Column(Integer, primary_key=True) > _instance_dict = Column(PickleType(mutable=True)) > name = Column(String) > > def __init__(self, name): > self._instance_dict = {} > self.name = name > > def __setattr__(self, key, value): > if (key in self.__dict__ or key in self.__class__.__dict__ or > not hasattr(self, '_sa_instance_state') or > 'AssociationProxy' in key): > # handle class-level attrs (like Columns) using default behavior > print 'setattr normal', key, value > Base.__setattr__(self, key, value) > else: > # regular python objects attached to as an attribute go to > pickled instance dict > print 'setattr other', key, value > self._instance_dict[key] = value > > def __getattr__(self, key): > # use default behavior for looking up the dicts themselves > if key == '_instance_dict' and key not in self.__dict__: > self._sa_instance_state.initialize(key) > return getattr(self, key) > > # check for the key in regular python object dict and sqlobjects dict > try: > return self._instance_dict[key] > except KeyError: > return Base.__getattr__(self, key)
you can do what you're doing here though I might want to make a more succinct system of determining which attributes go to __dict__ and which go into the special _instance_dict you have there. A good system for that kind of thing often includes things like this: 1. if the name has an underscore (_somename), it's not meant for the _instance_dict. This allows all the special things like _sa_instance_state and whatnot to not be interfered with. 2. to determine if the given key is "mapped", a quick and dirty way to check that is via the mapping: mapper = self.__mapper__ is_mapped = mapper.has_property(key) 3. The system for determining if a key is meant for _instance_dict or not is just in one place (like def _is_instance_dict_key()) so that __setattr__ and __getattr__ don't repeat themselves 4. the _is_instance_dict_key thing could also cache the yes/no answers in a dictionary tied to the class. hope this helps ! -- 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.