| 16 | | def writeSAObject(obj): |
| 17 | | """ |
| 18 | | Return a copy of the given sqlalchemy orm object. |
| 19 | | """ |
| 20 | | base_obj = obj |
| 21 | | |
| 22 | | # Create a copy of the object |
| 23 | | obj = copy.copy(obj) |
| 24 | | |
| 25 | | # Collect properties which are not 'known' |
| 26 | | properties = [property for property in obj.__dict__ |
| 27 | | if property not in getattr(obj, 'c', []) and |
| 28 | | property != "_sa_session_id"] |
| 29 | | |
| 30 | | # Iterate over the properties which are not columns of the orm mapped table |
| 31 | | for property in properties: |
| 32 | | # Delete 'private' properties |
| 33 | | if property.startswith('_'): |
| 34 | | del obj.__dict__[property] |
| | 12 | sa_properties = ('_state', '_sa_instance_state', '_sa_class_manager') |
| | 13 | |
| | 14 | def get_SA_attrs(obj): |
| | 15 | """Get attributes to be encoded for a SA object.""" |
| | 16 | if not is_SA_object(obj): |
| | 17 | return pyamf.util.native_get_attrs(obj) |
| | 18 | |
| | 19 | attrs = {} |
| | 20 | mapper = obj._sa_class_manager.mapper |
| | 21 | for property in mapper.iterate_properties: |
| | 22 | if property.key in sa_properties: |
| 36 | | |
| 37 | | # one-to-many references are stored in the InstrumentedList type |
| 38 | | # convert it to a list and search properties of each item to update the |
| 39 | | # backrefence of the object to the copied object |
| 40 | | value = obj.__dict__[property] |
| 41 | | if isinstance(value, sqlalchemy.orm.collections.InstrumentedList): |
| 42 | | value = list(value) |
| 43 | | |
| 44 | | # Update backreferences to the copied object |
| 45 | | for item in value: |
| 46 | | for item_property in item.__dict__: |
| 47 | | if item.__dict__[item_property] == base_obj: |
| 48 | | item.__dict__[item_property] = obj |
| 49 | | |
| 50 | | obj.__dict__[property] = value |
| 51 | | return obj |
| 52 | | |
| 53 | | def isSqlAlchemyObject(data): |
| 54 | | """ Detect if this object is registered in a sqlalchemy session. |
| 55 | | sqlalchemy 0.4 created a private variable '_state' in the object to |
| 56 | | store state information. in sqlalchemy 0.5 this is '_sa_instance_state' |
| 57 | | """ |
| 58 | | if not isinstance(data, object): |
| | 24 | |
| | 25 | if property.key.startswith('__'): |
| | 26 | continue |
| | 27 | |
| | 28 | attrs[property.key] = getattr(obj, property.key) |
| | 29 | return attrs |
| | 30 | |
| | 31 | def write_SA_collection(obj): |
| | 32 | """Convert a SQLAlchemy collection to a native type.""" |
| | 33 | if isinstance(obj, sqlalchemy.orm.collections.InstrumentedList): |
| | 34 | new_obj = [] |
| | 35 | new_obj.extend(obj) |
| | 36 | return new_obj |
| | 37 | if isinstance(obj, sqlalchemy.orm.collections.InstrumentedDict): |
| | 38 | new_obj = {} |
| | 39 | new_obj.update(obj) |
| | 40 | return new_obj |
| | 41 | if isinstance(obj, sqlalchemy.orm.collections.InstrumentedSet): |
| | 42 | new_obj = Set() |
| | 43 | new_obj.union(obj) |
| | 44 | return new_obj |
| | 45 | |
| | 46 | raise "Not a known SQLAlchemy collection type." |
| | 47 | |
| | 48 | def is_SA_object(obj): |
| | 49 | """Detect if this object is a SQLAlchemy instance.""" |
| | 50 | if is_SA_collection(obj): |
| 70 | | pyamf.add_type(isSqlAlchemyObject, writeSAObject) |
| | 53 | for property in sa_properties: |
| | 54 | if hasattr(obj, property): |
| | 55 | return True |
| | 56 | |
| | 57 | return False |
| | 58 | |
| | 59 | def is_SA_collection(obj): |
| | 60 | """Detect if this object is a SQLAlchemy collection.""" |
| | 61 | if isinstance(obj, sqlalchemy.orm.collections.InstrumentedList): |
| | 62 | return True |
| | 63 | if isinstance(obj, sqlalchemy.orm.collections.InstrumentedSet): |
| | 64 | return True |
| | 65 | if isinstance(obj, sqlalchemy.orm.collections.InstrumentedDict): |
| | 66 | return True |
| | 67 | return False |
| | 68 | |
| | 69 | # Replace this method with the adapter's |
| | 70 | pyamf.util.native_get_attrs = pyamf.util.get_attrs |
| | 71 | pyamf.util.get_attrs = get_SA_attrs |
| | 72 | # turn collections into native objects |
| | 73 | pyamf.add_type(is_SA_collection, write_SA_collection) |