Package pyamf :: Package adapters :: Module _sqlalchemy
[hide private]
[frames] | no frames]

Source Code for Module pyamf.adapters._sqlalchemy

  1  # Copyright (c) 2007-2009 The PyAMF Project. 
  2  # See LICENSE for details. 
  3   
  4  """ 
  5  SQLAlchemy adapter module. 
  6   
  7  @see: U{SQLAlchemy homepage (external)<http://www.sqlalchemy.org>} 
  8   
  9  @since: 0.4 
 10  """ 
 11   
 12  import sqlalchemy 
 13  from sqlalchemy.orm import collections 
 14   
 15  try: 
 16      from sqlalchemy.orm import class_mapper, object_mapper 
 17  except ImportError: 
 18      from sqlalchemy.orm.util import class_mapper, object_mapper 
 19   
 20  import pyamf 
 21  from pyamf.adapters import util 
 22   
 23  UnmappedInstanceError = None 
 24   
 25  try: 
 26      class_mapper(dict) 
 27  except Exception, e: 
 28      UnmappedInstanceError = e.__class__ 
 29   
30 -class SaMappedClassAlias(pyamf.ClassAlias):
31 KEY_ATTR = 'sa_key' 32 LAZY_ATTR = 'sa_lazy' 33 EXCLUDED_ATTRS = [ 34 '_sa_instance_state', '_sa_session_id', '_state', 35 '_entity_name', '_instance_key', '_sa_class_manager', 36 '_sa_adapter', '_sa_appender', '_sa_instrumented', 37 '_sa_iterator', '_sa_remover', '_sa_initiator', 38 ] 39
40 - def _getMapper(self, obj):
41 """ 42 Returns C{sqlalchemy.orm.mapper.Mapper} object. 43 """ 44 if hasattr(self, 'primary_mapper'): 45 return self.primary_mapper 46 47 try: 48 self.primary_mapper = object_mapper(obj) 49 except UnmappedInstanceError: 50 self.primary_mapper = None 51 52 return self.primary_mapper
53
54 - def getAttrs(self, obj, *args, **kwargs):
55 """ 56 Returns a C{tuple} containing 2 lists. The 1st is a list of allowed 57 static attribute names, and the 2nd is a list of allowed dynamic 58 attribute names. 59 """ 60 mapper = self._getMapper(obj) 61 62 if mapper is None: 63 return pyamf.ClassAlias.getAttrs(self, obj, *args, **kwargs) 64 65 if not hasattr(self, 'static_attrs'): 66 self.static_attrs = [self.KEY_ATTR, self.LAZY_ATTR] 67 self.properties = [] 68 69 for prop in mapper.iterate_properties: 70 self.static_attrs.append(prop.key) 71 72 for key, prop in self.klass.__dict__.iteritems(): 73 if isinstance(prop, property): 74 self.properties.append(key) 75 self.static_attrs.append(key) 76 77 dynamic_attrs = [] 78 79 for key in obj.__dict__.keys(): 80 if key in self.EXCLUDED_ATTRS: 81 continue 82 83 if key not in self.static_attrs: 84 dynamic_attrs.append(key) 85 86 return self.static_attrs, dynamic_attrs
87
88 - def getAttributes(self, obj, *args, **kwargs):
89 """ 90 Returns a C{tuple} containing a dict of static and dynamic attributes 91 for C{obj}. 92 """ 93 mapper = self._getMapper(obj) 94 95 if mapper is None: 96 return pyamf.ClassAlias.getAttributes(self, obj, *args, **kwargs) 97 98 static_attrs = {} 99 dynamic_attrs = {} 100 lazy_attrs = [] 101 102 static_attr_names, dynamic_attr_names = self.getAttrs(obj) 103 104 # primary_key_from_instance actually changes obj.__dict__ if 105 # primary key properties do not already exist in obj.__dict__ 106 static_attrs[self.KEY_ATTR] = mapper.primary_key_from_instance(obj) 107 108 for attr in static_attr_names: 109 if attr in obj.__dict__ or attr in self.properties: 110 static_attrs[attr] = getattr(obj, attr) 111 112 continue 113 114 if attr in [self.KEY_ATTR, self.LAZY_ATTR]: 115 continue 116 117 # attrs here are lazy but have not been loaded from the db yet .. 118 lazy_attrs.append(attr) 119 static_attrs[attr] = pyamf.Undefined 120 121 for attr in dynamic_attr_names: 122 if attr in obj.__dict__: 123 dynamic_attrs[attr] = getattr(obj, attr) 124 125 static_attrs[self.LAZY_ATTR] = lazy_attrs 126 127 return static_attrs, dynamic_attrs
128
129 - def applyAttributes(self, obj, attrs, *args, **kwargs):
130 """ 131 Add decoded attributes to instance. 132 """ 133 mapper = self._getMapper(obj) 134 135 if mapper is None: 136 pyamf.ClassAlias.applyAttributes(self, obj, attrs, *args, **kwargs) 137 138 return 139 140 # Delete lazy-loaded attrs. 141 # 142 # Doing it this way ensures that lazy-loaded attributes are not 143 # attached to the object, even if there is a default value specified 144 # in the __init__ method. 145 # 146 # This is the correct behavior, because SQLAlchemy ignores __init__. 147 # So, an object retreived from a DB with SQLAlchemy will not have a 148 # lazy-loaded value, even if __init__ specifies a default value. 149 if self.LAZY_ATTR in attrs: 150 obj_state = None 151 152 if hasattr(sqlalchemy.orm.attributes, 'instance_state'): 153 obj_state = sqlalchemy.orm.attributes.instance_state(obj) 154 155 for lazy_attr in attrs[self.LAZY_ATTR]: 156 if lazy_attr in obj.__dict__: 157 # Delete directly from the dict, so 158 # SA callbacks are not triggered. 159 del obj.__dict__[lazy_attr] 160 161 # Delete from committed_state so 162 # SA thinks this attribute was never modified. 163 # 164 # If the attribute was set in the __init__ method, 165 # SA will think it is modified and will try to update 166 # it in the database. 167 if obj_state is not None: 168 if lazy_attr in obj_state.committed_state: 169 del obj_state.committed_state[lazy_attr] 170 if lazy_attr in obj_state.dict: 171 del obj_state.dict[lazy_attr] 172 173 if lazy_attr in attrs: 174 del attrs[lazy_attr] 175 176 del attrs[self.LAZY_ATTR] 177 178 if self.KEY_ATTR in attrs: 179 del attrs[self.KEY_ATTR] 180 181 for key, prop in self.klass.__dict__.iteritems(): 182 if isinstance(prop, property) and key in attrs.keys(): 183 if prop.fset is None: 184 del attrs[key] 185 186 pyamf.util.set_attrs(obj, attrs)
187
188 -def is_class_sa_mapped(klass):
189 """ 190 @rtype: C{bool} 191 """ 192 if not isinstance(klass, type): 193 klass = type(klass) 194 195 try: 196 class_mapper(klass) 197 except UnmappedInstanceError: 198 return False 199 200 return True
201 202 pyamf.register_alias_type(SaMappedClassAlias, is_class_sa_mapped) 203 204 pyamf.add_type(collections.InstrumentedList, util.to_list) 205 pyamf.add_type(collections.InstrumentedDict, util.to_dict) 206 pyamf.add_type(collections.InstrumentedSet, util.to_set) 207