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