Xavier (Open ERP) has proposed merging 
lp:~openerp-dev/openobject-server/trunk-fix-o2m-in-contexts-xmo into 
lp:openobject-server.

Requested reviews:
  Vo Minh Thu (OpenERP) (vmt-openerp)
  Olivier Dony (OpenERP) (odo-openerp)

For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-fix-o2m-in-contexts-xmo/+merge/78845

Replaces ad-hoc handling of o2m commands sequences in res.partner.bank with a 
new method handling this processing on BaseModel.

Also add some tests to try to ensure the method behaves as expected

Future outlook: calls to account_voucher.account_voucher.resolve_o2m_operations 
(there are 3 calls, for the `line_ids`, `line_cr_ids` and `line_dr_ids`)  
should be replaced by calls to this method.
-- 
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-fix-o2m-in-contexts-xmo/+merge/78845
Your team OpenERP R&D Team is subscribed to branch 
lp:~openerp-dev/openobject-server/trunk-fix-o2m-in-contexts-xmo.
=== modified file 'openerp/addons/base/res/res_bank.py'
--- openerp/addons/base/res/res_bank.py	2011-10-10 08:10:21 +0000
+++ openerp/addons/base/res/res_bank.py	2011-10-10 14:03:09 +0000
@@ -108,14 +108,8 @@
         if not context.get('address'):
             return value
 
-        for _, id, address_data in context['address']:
-            if not (id or address): continue
-            address = {}
-            if id:
-                address.update(self.pool['res.partner.address']
-                    .read(cursor, user, [id], ['type', field], context=context)[0])
-            if address_data:
-                address.update(address_data)
+        for address in self.pool.get('res.partner').serialize_o2m_commands(
+            cursor, user, 'address', context['address'], ['type', field], context=context):
 
             if address.get('type') == 'default':
                 return address.get(field, value)

=== modified file 'openerp/osv/orm.py'
--- openerp/osv/orm.py	2011-10-07 14:49:44 +0000
+++ openerp/osv/orm.py	2011-10-10 14:03:09 +0000
@@ -4762,6 +4762,50 @@
 
         return True
 
+    def serialize_o2m_commands(self, cr, uid, field_name, o2m_commands, fields=None, context=None):
+        """ Serializes o2m commands into record dictionaries (as if
+        all the o2m records came from the database via a read()), and
+        returns an iterator over these dictionaries.
+
+        Because o2m commands might be creation commands, not all
+        record ids will contain an ``id`` field. Commands matching an
+        existing record (UPDATE and LINK_TO) will have an id.
+
+        :param field_name: name of the o2m field matching the commands
+        :type field_name: str
+        :param o2m_commands: one2many commands to execute on ``field_name``
+        :type o2m_commands: list((int|False, int|False, dict|False))
+        :param fields: list of fields to read from the database, when applicable
+        :type fields: list(str)
+        :param context: request context
+        :returns: o2m records in a shape similar to that returned by
+                  ``read()`` (except records may be missing the ``id``
+                  field if they don't exist in db)
+        :rtype: iter(dict)
+        """
+        o2m_model = self._all_columns[field_name].column._obj
+
+        # extract and handle case of single ids (instead of commands):
+        # convert to LINK_TO commands (4)
+        c1, c2 = itertools.tee(o2m_commands)
+        commands = list(itertools.chain(
+            (command for command in c1 if isinstance(command, (list, tuple))),
+            ((4, id, None) for id in c2 if not isinstance(id, (list, tuple)))))
+
+        # extract records to read, by id, in a mapping dict
+        ids_to_read = [id for (command, id, _) in commands if command in (1, 4)]
+        records_by_id = dict(
+            (record['id'], record)
+            for record in self.pool.get(o2m_model).read(
+                cr, uid, ids_to_read, fields=fields, context=context))
+
+        # merge record from db with record provided by command
+        for command, id, record in commands:
+            item = {}
+            if command in (1, 4): item.update(records_by_id[id])
+            if command in (0, 1): item.update(record)
+            yield item
+
 # keep this import here, at top it will cause dependency cycle errors
 import expression
 

=== added file 'tests/test_orm.py'
--- tests/test_orm.py	1970-01-01 00:00:00 +0000
+++ tests/test_orm.py	2011-10-10 14:03:09 +0000
@@ -0,0 +1,121 @@
+import os
+import unittest2
+import openerp
+
+UID = 1
+DB = os.environ['OPENERP_DATABASE']
+
+CREATE = lambda values: (0, False, values)
+UPDATE = lambda id, values: (1, id, values)
+LINK_TO = lambda id: (4, id, False)
+
+
+def setUpModule():
+    openerp.tools.config['addons_path'] = os.environ['OPENERP_ADDONS_PATH']
+
+class TestO2MSerialization(unittest2.TestCase):
+    def setUp(self):
+        self.cr = openerp.modules.registry.RegistryManager.get(DB).db.cursor()
+        self.partner = openerp.modules.registry.RegistryManager.get(DB)['res.partner']
+        self.address = openerp.modules.registry.RegistryManager.get(DB)['res.partner.address']
+    def tearDown(self):
+        self.cr.rollback()
+        self.cr.close()
+
+    def test_no_command(self):
+        " empty list of commands yields an empty list of records "
+        results = list(self.partner.serialize_o2m_commands(self.cr, UID, 'address', []))
+
+        self.assertEqual(results, [])
+
+    def test_CREATE_commands(self):
+        " returns the VALUES dict as-is "
+        results = list(self.partner.serialize_o2m_commands(
+            self.cr, UID, 'address',
+            map(CREATE, [{'foo': 'bar'}, {'foo': 'baz'}, {'foo': 'baq'}])))
+        self.assertEqual(results, [
+            {'foo': 'bar'},
+            {'foo': 'baz'},
+            {'foo': 'baq'}
+        ])
+
+    def test_LINK_TO_command(self):
+        " reads the records from the database, records are returned with their ids. "
+        ids = [
+            self.address.create(self.cr, UID, {'name': 'foo'}),
+            self.address.create(self.cr, UID, {'name': 'bar'}),
+            self.address.create(self.cr, UID, {'name': 'baz'})
+        ]
+        commands = map(LINK_TO, ids)
+
+        results = list(self.partner.serialize_o2m_commands(self.cr, UID, 'address', commands, ['name']))
+
+        self.assertEqual(results, [
+            {'id': ids[0], 'name': 'foo'},
+            {'id': ids[1], 'name': 'bar'},
+            {'id': ids[2], 'name': 'baz'}
+        ])
+
+    def test_bare_ids_command(self):
+        " same as the equivalent LINK_TO commands "
+        ids = [
+            self.address.create(self.cr, UID, {'name': 'foo'}),
+            self.address.create(self.cr, UID, {'name': 'bar'}),
+            self.address.create(self.cr, UID, {'name': 'baz'})
+        ]
+
+        results = list(self.partner.serialize_o2m_commands(self.cr, UID, 'address', ids, ['name']))
+
+        self.assertEqual(results, [
+            {'id': ids[0], 'name': 'foo'},
+            {'id': ids[1], 'name': 'bar'},
+            {'id': ids[2], 'name': 'baz'}
+        ])
+
+    def test_UPDATE_command(self):
+        " take the in-db records and merge the provided information in "
+        id_foo = self.address.create(self.cr, UID, {'name': 'foo'})
+        id_bar = self.address.create(self.cr, UID, {'name': 'bar'})
+        id_baz = self.address.create(self.cr, UID, {'name': 'baz', 'city': 'tag'})
+
+        results = list(self.partner.serialize_o2m_commands(
+            self.cr, UID, 'address', [
+                LINK_TO(id_foo),
+                UPDATE(id_bar, {'name': 'qux', 'city': 'tagtag'}),
+                UPDATE(id_baz, {'name': 'quux'})
+            ], ['name', 'city']))
+
+        self.assertEqual(results, [
+            {'id': id_foo, 'name': 'foo', 'city': False},
+            {'id': id_bar, 'name': 'qux', 'city': 'tagtag'},
+            {'id': id_baz, 'name': 'quux', 'city': 'tag'}
+        ])
+
+    def test_mixed_commands(self):
+        ids = [
+            self.address.create(self.cr, UID, {'name': name})
+            for name in ['NObar', 'baz', 'qux', 'NOquux', 'NOcorge', 'garply']
+        ]
+
+        results = list(self.partner.serialize_o2m_commands(
+            self.cr, UID, 'address', [
+                CREATE({'name': 'foo'}),
+                UPDATE(ids[0], {'name': 'bar'}),
+                LINK_TO(ids[1]),
+                LINK_TO(ids[2]),
+                UPDATE(ids[3], {'name': 'quux',}),
+                UPDATE(ids[4], {'name': 'corge'}),
+                CREATE({'name': 'grault'}),
+                LINK_TO(ids[5])
+            ], ['name']))
+
+        self.assertEqual(results, [
+            {'name': 'foo'},
+            {'id': ids[0], 'name': 'bar'},
+            {'id': ids[1], 'name': 'baz'},
+            {'id': ids[2], 'name': 'qux'},
+            {'id': ids[3], 'name': 'quux'},
+            {'id': ids[4], 'name': 'corge'},
+            {'name': 'grault'},
+            {'id': ids[5], 'name': 'garply'}
+        ])

_______________________________________________
Mailing list: https://launchpad.net/~openerp-dev-gtk
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~openerp-dev-gtk
More help   : https://help.launchpad.net/ListHelp

Reply via email to