Ticket #277: pyamf_sqlalchemy_adapter.diff

File pyamf_sqlalchemy_adapter.diff, 9.3 KB (added by mvtellingen, 4 months ago)

update to trunk (r1525)

  • THANKS.txt

     
    33Joachim Bauch                  jojo@struktur.de 
    44Antti Kaihola                  akaihol+pyamf@ambitone.com 
    55Jacob Feisley                  jacob.feisley@gmail.com 
     6Michael van Tellingen          m.vantellingen@auto-interactive.nl 
    67 
    78And everyone who keeps sending feedback, helping us improve PyAMF. 
     9 No newline at end of file 
  • pyamf/tests/adapters/test_sqlalchemy.py

     
     1# Copyright (c) 2007-2008 The PyAMF Project. 
     2# See LICENSE for details. 
     3 
     4""" 
     5PyAMF SQLAlchemy adapter tests. 
     6 
     7@since 0.4.0 
     8""" 
     9import datetime 
     10import sys 
     11import unittest 
     12 
     13import sqlalchemy 
     14from sqlalchemy import MetaData, Table, Column, Integer, String, ForeignKey, \ 
     15                       create_engine  
     16from sqlalchemy.orm import mapper, relation, sessionmaker 
     17 
     18import pyamf 
     19import pyamf.flex 
     20 
     21class SATestCase(unittest.TestCase): 
     22         
     23    def test_model(self): 
     24        my_sa_user = self.User() 
     25        my_sa_user.name = "Test user" 
     26        self.session.save(my_sa_user) 
     27 
     28        my_user = self.User() 
     29        my_user.name = "Test user" 
     30         
     31        self.session.commit() 
     32         
     33        encoder = pyamf.get_encoder(pyamf.AMF3) 
     34        encoder.writeElement(my_user) 
     35        stream_value = encoder.stream.getvalue() 
     36         
     37        encoder = pyamf.get_encoder(pyamf.AMF3) 
     38        encoder.writeElement(my_sa_user)                         
     39        encoder.stream.getvalue() 
     40 
     41    def test_array_collection(self): 
     42        for i in range(0, 20): 
     43            my_user = self.User() 
     44            my_user.name = "Test user %d" % i 
     45            self.session.save(my_user) 
     46         
     47        users = self.session.query(self.User).all() 
     48         
     49        ac = pyamf.flex.ArrayCollection([user for user in users]) 
     50 
     51        encoder = pyamf.get_encoder(pyamf.AMF3) 
     52        encoder.writeElement(ac) 
     53        encoder.stream.getvalue() 
     54 
     55    def test_complex_structure(self): 
     56        user = self.User(name="test-user") 
     57        user.addresses.append(self.Address(email_address="test@example.org")) 
     58        for i, string in enumerate(['one', 'two', 'three']): 
     59            addr = self.Address(email_address="%s@example.org" % string) 
     60            addr.info.append(self.AddressInfo(data="%s-%d" % (string, i))) 
     61            user.addresses.append(addr) 
     62         
     63        encoder = pyamf.get_encoder(pyamf.AMF3) 
     64        encoder.writeElement(user) 
     65        data = encoder.stream.getvalue() 
     66 
     67        expected_data = ( 
     68            '\n\x0b\x01\tname\x06\x13test-user\x13addresses\t\t\x01\n\x0b\x01' 
     69            '\x1bemail_address\x06!test@example.org\tuser\n\x00\x01\n\x0b\x01' 
     70            '\tinfo\t\x03\x01\n\x0b\x01\tdata\x06\x0bone-0\x0faddress\n\x06' 
     71            '\x01\x06\x06\x1fone@example.org\n\n\x00\x01\n\x0b\x01\x0c\t\x03' 
     72            '\x01\n\x0b\x01\x0e\x06\x0btwo-1\x12\n\x0c\x01\x06\x06\x1ftwo@examp' 
     73            'le.org\n\n\x00\x01\n\x0b\x01\x0c\t\x03\x01\n\x0b\x01\x0e\x06\x0fth' 
     74            'ree-2\x12\n\x12\x01\x06\x06#three@example.org\n\n\x00\x01\x01') 
     75         
     76        self.assertEqual(data, expected_data) 
     77         
     78         
     79    def setUp(self): 
     80        metadata = MetaData() 
     81        engine = create_engine('sqlite:///:memory:', echo=False) 
     82         
     83        Session = sessionmaker(bind=engine) 
     84         
     85        self.session = Session() 
     86         
     87        users_table = Table('users_table', metadata, 
     88                        Column('id', Integer, primary_key=True), 
     89                        Column('name', String(64))) 
     90 
     91        addresses_table = Table('addresses_table', metadata, 
     92                            Column('id', Integer, primary_key=True), 
     93                            Column('user_id', Integer, 
     94                                   ForeignKey('users_table.id')), 
     95                            Column('email_address', String(128))) 
     96 
     97        address_information = Table('addrinfo_table', metadata, 
     98                                Column('id', Integer, primary_key=True), 
     99                                Column('addr_id', Integer, 
     100                                       ForeignKey('addresses_table.id')), 
     101                                Column('data', String(128))) 
     102         
     103                                 
     104        mapper(self.User, users_table, properties=dict( 
     105               addresses=relation(self.Address, backref='user'), 
     106               )) 
     107       
     108        mapper(self.Address, addresses_table, properties=dict( 
     109                info=relation(self.AddressInfo, backref='address'), 
     110                )) 
     111         
     112        mapper(self.AddressInfo, address_information) 
     113         
     114        metadata.create_all(engine) 
     115         
     116         
     117    def tearDown(self): 
     118        from sqlalchemy.orm import clear_mappers 
     119        clear_mappers() 
     120             
     121 
     122    class User(object): 
     123        def __init__(self, **kwargs): 
     124            self.__dict__.update(kwargs) 
     125 
     126    class Address(object): 
     127        def __init__(self, **kwargs): 
     128            self.__dict__.update(kwargs) 
     129 
     130    class AddressInfo(object): 
     131        def __init__(self, **kwargs): 
     132            self.__dict__.update(kwargs) 
     133 
     134             
     135def suite(): 
     136    suite = unittest.TestSuite() 
     137 
     138    suite.addTest(unittest.makeSuite(SATestCase)) 
     139 
     140    return suite 
     141 
     142if __name__ == '__main__': 
     143    unittest.main(defaultTest='suite') 
     144 No newline at end of file 
  • pyamf/adapters/_sqlalchemy.py

     
     1# Copyright (c) 2007-2008 The PyAMF Project. 
     2# See LICENSE for details. 
     3 
     4""" 
     5SQLAlchemy adapter (experimental!) 
     6 
     7@since: 0.4.0 
     8""" 
     9import copy 
     10 
     11import sqlalchemy 
     12import sqlalchemy.orm.session 
     13 
     14import pyamf 
     15 
     16def 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] 
     35            continue 
     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 
     53def 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): 
     59        return False 
     60     
     61    # sqlalchemy 0.4 detection     
     62    state = getattr(data, '_state', None) 
     63     
     64    # sqlalchemy 0.5 ? 
     65    if not state: 
     66        state = getattr(data, '_sa_instance_state', None) 
     67         
     68    return isinstance(state, sqlalchemy.orm.attributes.InstanceState) 
     69 
     70pyamf.add_type(isSqlAlchemyObject, writeSAObject) 
  • pyamf/__init__.py

     
    595595        except AttributeError: 
    596596            return self._getWriteElementFunc(data) 
    597597 
     598        func = self._getWriteElementFunc(data) 
     599         
     600        # Don't cache CustomTypeFunc objects! 
     601        if isinstance(func, CustomTypeFunc): 
     602            return func 
     603         
    598604        if key not in self._write_elem_func_cache: 
    599             self._write_elem_func_cache[key] =  self._getWriteElementFunc(data) 
     605            self._write_elem_func_cache[key] =  func 
    600606 
    601607        return self._write_elem_func_cache[key] 
    602608 
  • pyamf/amf3.py

     
    12381238                    func(data, use_references=use_references) 
    12391239            except (KeyboardInterrupt, SystemExit): 
    12401240                raise 
    1241             except: 
     1241            except Exception, e: 
    12421242                raise pyamf.EncodeError, "Unable to encode '%r'" % data 
    12431243 
    12441244    def writeType(self, type): 
     
    14091409        @param  use_references: 
    14101410        """ 
    14111411        self.writeType(ASTypes.ARRAY) 
    1412  
     1412         
    14131413        if use_references is True: 
    14141414            try: 
    14151415                ref = self.context.getObjectReference(n)