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 68 for prop in mapper.iterate_properties: 69 self.static_attrs.append(prop.key) 70 71 dynamic_attrs = [] 72 73 for key in obj.__dict__.keys(): 74 if key in self.EXCLUDED_ATTRS: 75 continue 76 77 if key not in self.static_attrs: 78 dynamic_attrs.append(key) 79 80 return self.static_attrs, dynamic_attrs
81
82 - def getAttributes(self, obj, *args, **kwargs):
83 """ 84 Returns a C{tuple} containing a dict of static and dynamic attributes 85 for C{obj}. 86 """ 87 mapper = self._getMapper(obj) 88 89 if mapper is None: 90 return pyamf.ClassAlias.getAttributes(self, obj, *args, **kwargs) 91 92 static_attrs = {} 93 dynamic_attrs = {} 94 lazy_attrs = [] 95 96 static_attr_names, dynamic_attr_names = self.getAttrs(obj) 97 98 # primary_key_from_instance actually changes obj.__dict__ if 99 # primary key properties do not already exist in obj.__dict__ 100 static_attrs[self.KEY_ATTR] = mapper.primary_key_from_instance(obj) 101 102 for attr in static_attr_names: 103 if attr in obj.__dict__: 104 static_attrs[attr] = getattr(obj, attr) 105 106 continue 107 108 if attr in [self.KEY_ATTR, self.LAZY_ATTR]: 109 continue 110 111 # attrs here are lazy but have not been loaded from the db yet .. 112 lazy_attrs.append(attr) 113 static_attrs[attr] = pyamf.Undefined 114 115 for attr in dynamic_attr_names: 116 if attr in obj.__dict__: 117 dynamic_attrs[attr] = getattr(obj, attr) 118 119 static_attrs[self.LAZY_ATTR] = lazy_attrs 120 121 return static_attrs, dynamic_attrs
122
123 - def applyAttributes(self, obj, attrs, *args, **kwargs):
124 """ 125 Add decoded attributes to instance. 126 """ 127 mapper = self._getMapper(obj) 128 129 if mapper is None: 130 pyamf.ClassAlias.applyAttributes(self, obj, attrs, *args, **kwargs) 131 132 return 133 134 # Delete lazy-loaded attrs. 135 # 136 # Doing it this way ensures that lazy-loaded attributes are not 137 # attached to the object, even if there is a default value specified 138 # in the __init__ method. 139 # 140 # This is the correct behavior, because SQLAlchemy ignores __init__. 141 # So, an object retreived from a DB with SQLAlchemy will not have a 142 # lazy-loaded value, even if __init__ specifies a default value. 143 if self.LAZY_ATTR in attrs: 144 obj_state = None 145 146 if hasattr(sqlalchemy.orm.attributes, 'instance_state'): 147 obj_state = sqlalchemy.orm.attributes.instance_state(obj) 148 149 for lazy_attr in attrs[self.LAZY_ATTR]: 150 if lazy_attr in obj.__dict__: 151 # Delete directly from the dict, so 152 # SA callbacks are not triggered. 153 del obj.__dict__[lazy_attr] 154 155 # Delete from committed_state so 156 # SA thinks this attribute was never modified. 157 # 158 # If the attribute was set in the __init__ method, 159 # SA will think it is modified and will try to update 160 # it in the database. 161 if obj_state is not None: 162 if lazy_attr in obj_state.committed_state: 163 del obj_state.committed_state[lazy_attr] 164 if lazy_attr in obj_state.dict: 165 del obj_state.dict[lazy_attr] 166 167 if lazy_attr in attrs: 168 del attrs[lazy_attr] 169 170 del attrs[self.LAZY_ATTR] 171 172 if self.KEY_ATTR in attrs: 173 del attrs[self.KEY_ATTR] 174 175 pyamf.util.set_attrs(obj, attrs)
176
177 -def is_class_sa_mapped(klass):
178 """ 179 @rtype: C{bool} 180 """ 181 if not isinstance(klass, type): 182 klass = type(klass) 183 184 try: 185 class_mapper(klass) 186 except UnmappedInstanceError: 187 return False 188 189 return True
190 191 pyamf.register_alias_type(SaMappedClassAlias, is_class_sa_mapped) 192 193 pyamf.add_type(collections.InstrumentedList, util.to_list) 194 pyamf.add_type(collections.InstrumentedDict, util.to_dict) 195 pyamf.add_type(collections.InstrumentedSet, util.to_set) 196