[ADD] method to correctly serialize o2m commands into a sequence of record-dicts for e.g. onchanges

also alters res.partner.bank _default_value to use it with res.partner addresses, and adds a few tests to ensure behavior is correct

bzr revid: xmo@openerp.com-20111010135157-dafgcwwcni5gkeom
This commit is contained in:
Xavier Morel 2011-10-10 15:51:57 +02:00
parent 8a5314b15e
commit d5accc0fae
3 changed files with 167 additions and 8 deletions

View File

@ -108,14 +108,8 @@ class res_partner_bank(osv.osv):
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)

View File

@ -4762,6 +4762,50 @@ class BaseModel(object):
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

121
tests/test_orm.py Normal file
View File

@ -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'}
])