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

# Source Code for Module pyamf.adapters._sqlalchemy_orm

      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  from sqlalchemy import orm, __version__
     13
     14  try:
     15      from sqlalchemy.orm import class_mapper
     16  except ImportError:
     17      from sqlalchemy.orm.util import class_mapper
     18
     19  import pyamf
     20
     21  UnmappedInstanceError = None
     22
     23  try:
     24      class_mapper(dict)
     25  except Exception, e:
     26      UnmappedInstanceError = e.__class__
     27
     28
     29 -class SaMappedClassAlias(pyamf.ClassAlias):
     30      KEY_ATTR = 'sa_key'
     31      LAZY_ATTR = 'sa_lazy'
     32      EXCLUDED_ATTRS = [
     33          '_entity_name', '_instance_key', '_sa_adapter', '_sa_appender',
     34          '_sa_class_manager', '_sa_initiator', '_sa_instance_state',
     35          '_sa_instrumented', '_sa_iterator', '_sa_remover', '_sa_session_id',
     36          '_state'
     37      ]
     38
     39      STATE_ATTR = '_sa_instance_state'
     40
     41      if __version__.startswith('0.4'):
     42          STATE_ATTR = '_state'
     43
     44 -    def getCustomProperties(self):
     45          self.mapper = class_mapper(self.klass)
     46          self.exclude_attrs.update(self.EXCLUDED_ATTRS)
     47
     48          self.properties = []
     49
     50          for prop in self.mapper.iterate_properties:
     51              self.properties.append(prop.key)
     52
     53          self.encodable_properties.update(self.properties)
     54          self.decodable_properties.update(self.properties)
     55          self.static_attrs.update(self.properties)
     56
     57 -    def getEncodableAttributes(self, obj, **kwargs):
     58          """
     59          Returns a C{tuple} containing a dict of static and dynamic attributes
     60          for C{obj}.
     61          """
     62          sa, da = pyamf.ClassAlias.getEncodableAttributes(self, obj, **kwargs)
     63
     64          if not da:
     65              da = {}
     66
     67          lazy_attrs = []
     68
     69          # primary_key_from_instance actually changes obj.__dict__ if
     70          # primary key properties do not already exist in obj.__dict__
     71          da[self.KEY_ATTR] = self.mapper.primary_key_from_instance(obj)
     72
     73          for attr in self.properties:
     74              if attr not in obj.__dict__:
     75                  lazy_attrs.append(attr)
     76
     77          da[self.LAZY_ATTR] = lazy_attrs
     78
     79          return sa, da
     80
     81 -    def getDecodableAttributes(self, obj, attrs, **kwargs):
     82          """
     83          """
     84          attrs = pyamf.ClassAlias.getDecodableAttributes(self, obj, attrs, **kwargs)
     85
     86          # Delete lazy-loaded attrs.
     87          #
     88          # Doing it this way ensures that lazy-loaded attributes are not
     89          # attached to the object, even if there is a default value specified
     90          # in the __init__ method.
     91          #
     92          # This is the correct behavior, because SQLAlchemy ignores __init__.
     93          # So, an object retreived from a DB with SQLAlchemy will not have a
     94          # lazy-loaded value, even if __init__ specifies a default value.
     95          if self.LAZY_ATTR in attrs:
     96              obj_state = None
     97
     98              if hasattr(orm.attributes, 'instance_state'):
     99                  obj_state = orm.attributes.instance_state(obj)
    100
    101              for lazy_attr in attrs[self.LAZY_ATTR]:
    102                  if lazy_attr in obj.__dict__:
    103                      # Delete directly from the dict, so
    104                      # SA callbacks are not triggered.
    105                      del obj.__dict__[lazy_attr]
    106
    107                  # Delete from committed_state so SA thinks this attribute was
    108                  # never modified.
    109                  #
    110                  # If the attribute was set in the __init__ method,
    111                  # SA will think it is modified and will try to update
    112                  # it in the database.
    113                  if obj_state is not None:
    114                      if lazy_attr in obj_state.committed_state:
    115                          del obj_state.committed_state[lazy_attr]
    116                      if lazy_attr in obj_state.dict:
    117                          del obj_state.dict[lazy_attr]
    118
    119                  if lazy_attr in attrs:
    120                      del attrs[lazy_attr]
    121
    122              del attrs[self.LAZY_ATTR]
    123
    124          if self.KEY_ATTR in attrs:
    125              del attrs[self.KEY_ATTR]
    126
    127          return attrs
    128
    129
    130 -def is_class_sa_mapped(klass):
    131      """
    132      @rtype: C{bool}
    133      """
    134      if not isinstance(klass, type):
    135          klass = type(klass)
    136
    137      try:
    138          class_mapper(klass)
    139      except UnmappedInstanceError:
    140          return False
    141
    142      return True
    143
    144  pyamf.register_alias_type(SaMappedClassAlias, is_class_sa_mapped)
    145