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.

Reply via email to