[IMP] orm: fix and generalize method resolve_o2m_commands_to_record_dicts (now named resolve_2many_commands)

bzr revid: rco@openerp.com-20120906144836-v2ye3o8n12iir84b
This commit is contained in:
Raphael Collet 2012-09-06 16:48:36 +02:00
parent 690113ffe2
commit f4945c2fc7
4 changed files with 59 additions and 68 deletions

View File

@ -109,7 +109,7 @@ class res_partner_bank(osv.osv):
if not context.get('address'): if not context.get('address'):
return value return value
for address in self.pool.get('res.partner').resolve_o2m_commands_to_record_dicts( for address in self.pool.get('res.partner').resolve_2many_commands(
cursor, user, 'address', context['address'], ['type', field], context=context): cursor, user, 'address', context['address'], ['type', field], context=context):
if address.get('type') == 'default': if address.get('type') == 'default':

View File

@ -159,7 +159,7 @@ class res_company(osv.osv):
])) ]))
# second line: bank accounts # second line: bank accounts
accounts = self.resolve_o2m_commands_to_record_dicts(cr, uid, 'bank_ids', bank_ids, context=context) accounts = self.resolve_2many_commands(cr, uid, 'bank_ids', bank_ids, context=context)
accounts_names = [('%(bank_name)s %(acc_number)s' % acc) for acc in accounts if acc['footer']] accounts_names = [('%(bank_name)s %(acc_number)s' % acc) for acc in accounts if acc['footer']]
if accounts_names: if accounts_names:
title = _('Bank Accounts') if len(accounts_names) > 1 else _('Bank Account') title = _('Bank Accounts') if len(accounts_names) > 1 else _('Bank Account')

View File

@ -5009,66 +5009,57 @@ class BaseModel(object):
return True return True
def resolve_o2m_commands_to_record_dicts(self, cr, uid, field_name, o2m_commands, fields=None, context=None): def resolve_2many_commands(self, cr, uid, field_name, commands, fields=None, context=None):
""" Serializes o2m commands into record dictionaries (as if """ Serializes one2many and many2many commands into record dictionaries
all the o2m records came from the database via a read()), and (as if all the records came from the database via a read()). This
returns an iterable over these dictionaries. method is aimed at onchange methods on one2many and many2many fields.
Because o2m commands might be creation commands, not all Because commands might be creation commands, not all record dicts
record ids will contain an ``id`` field. Commands matching an will contain an ``id`` field. Commands matching an existing record
existing record (``UPDATE`` and ``LINK_TO``) will have an id. will have an ``id``.
.. note:: ``CREATE``, ``UPDATE`` and ``LINK_TO`` stand for the :param field_name: name of the one2many or many2many field matching the commands
o2m command codes ``0``, ``1`` and ``4`` :type field_name: str
respectively :param commands: one2many or many2many commands to execute on ``field_name``
:type commands: list((int|False, int|False, dict|False))
:param field_name: name of the o2m field matching the commands :param fields: list of fields to read from the database, when applicable
:type field_name: str :type fields: list(str)
:param o2m_commands: one2many commands to execute on ``field_name`` :returns: records in a shape similar to that returned by ``read()``
:type o2m_commands: list((int|False, int|False, dict|False)) (except records may be missing the ``id`` field if they don't exist in db)
:param fields: list of fields to read from the database, when applicable :rtype: list(dict)
:type fields: list(str)
:raises AssertionError: if a command is not ``CREATE``, ``UPDATE`` or ``LINK_TO``
: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: ``list(dict)``
""" """
o2m_model = self._all_columns[field_name].column._obj result = [] # result (list of dict)
record_ids = set() # ids of records to read
updates = {} # {id: dict} of updates on particular records
# convert single ids and pairs to tripled commands for command in commands:
commands = [] if not isinstance(command, (list, tuple)):
for o2m_command in o2m_commands: record_ids.add(command)
if not isinstance(o2m_command, (list, tuple)): elif command[0] == 0:
command = 4 result.append(command[2])
commands.append((command, o2m_command, False)) elif command[0] == 1:
elif len(o2m_command) == 1: record_ids.add(command[1])
(command,) = o2m_command updates.setdefault(command[1], {}).update(command[2])
commands.append((command, False, False)) elif command[0] in (2, 3):
elif len(o2m_command) == 2: record_ids.discard(command[1])
command, id = o2m_command elif command[0] == 4:
commands.append((command, id, False)) record_ids.add(command[1])
else: elif command[0] == 5:
command = o2m_command[0] result, record_ids = [], []
commands.append(o2m_command) elif command[0] == 6:
assert command in (0, 1, 4), \ result, record_ids = [], list(command[2])
"Only CREATE, UPDATE and LINK_TO commands are supported in resolver"
# extract records to read, by id, in a mapping dict # read the records and apply the updates
ids_to_read = [id for (command, id, _) in commands if command in (1, 4)] other_model = self.pool.get(self._all_columns[field_name].column._obj)
records_by_id = dict( for record in other_model.read(cr, uid, list(record_ids), fields=fields, context=context):
(record['id'], record) record.update(updates.get(record['id'], {}))
for record in self.pool.get(o2m_model).read( result.append(record)
cr, uid, ids_to_read, fields=fields, context=context))
record_dicts = [] return result
# merge record from db with record provided by command
for command, id, record in commands: # for backward compatibility
item = {} def resolve_2many_commands(self, cr, uid, field_name, o2m_commands, fields=None, context=None):
if command in (1, 4): item.update(records_by_id[id]) return self.resolve_2many_commands(cr, uid, field_name, o2m_commands, fields, context)
if command in (0, 1): item.update(record)
record_dicts.append(item)
return record_dicts
# keep this import here, at top it will cause dependency cycle errors # keep this import here, at top it will cause dependency cycle errors
import expression import expression

View File

@ -23,14 +23,14 @@ class TestO2MSerialization(common.TransactionCase):
def test_no_command(self): def test_no_command(self):
" empty list of commands yields an empty list of records " " empty list of commands yields an empty list of records "
results = self.partner.resolve_o2m_commands_to_record_dicts( results = self.partner.resolve_2many_commands(
self.cr, UID, 'address', []) self.cr, UID, 'address', [])
self.assertEqual(results, []) self.assertEqual(results, [])
def test_CREATE_commands(self): def test_CREATE_commands(self):
" returns the VALUES dict as-is " " returns the VALUES dict as-is "
results = self.partner.resolve_o2m_commands_to_record_dicts( results = self.partner.resolve_2many_commands(
self.cr, UID, 'address', self.cr, UID, 'address',
map(CREATE, [{'foo': 'bar'}, {'foo': 'baz'}, {'foo': 'baq'}])) map(CREATE, [{'foo': 'bar'}, {'foo': 'baz'}, {'foo': 'baq'}]))
self.assertEqual(results, [ self.assertEqual(results, [
@ -48,7 +48,7 @@ class TestO2MSerialization(common.TransactionCase):
] ]
commands = map(LINK_TO, ids) commands = map(LINK_TO, ids)
results = self.partner.resolve_o2m_commands_to_record_dicts( results = self.partner.resolve_2many_commands(
self.cr, UID, 'address', commands, ['name']) self.cr, UID, 'address', commands, ['name'])
self.assertEqual(results, [ self.assertEqual(results, [
@ -65,7 +65,7 @@ class TestO2MSerialization(common.TransactionCase):
self.partner.create(self.cr, UID, {'name': 'baz'}) self.partner.create(self.cr, UID, {'name': 'baz'})
] ]
results = self.partner.resolve_o2m_commands_to_record_dicts( results = self.partner.resolve_2many_commands(
self.cr, UID, 'address', ids, ['name']) self.cr, UID, 'address', ids, ['name'])
self.assertEqual(results, [ self.assertEqual(results, [
@ -80,7 +80,7 @@ class TestO2MSerialization(common.TransactionCase):
id_bar = self.partner.create(self.cr, UID, {'name': 'bar'}) id_bar = self.partner.create(self.cr, UID, {'name': 'bar'})
id_baz = self.partner.create(self.cr, UID, {'name': 'baz', 'city': 'tag'}) id_baz = self.partner.create(self.cr, UID, {'name': 'baz', 'city': 'tag'})
results = self.partner.resolve_o2m_commands_to_record_dicts( results = self.partner.resolve_2many_commands(
self.cr, UID, 'address', [ self.cr, UID, 'address', [
LINK_TO(id_foo), LINK_TO(id_foo),
UPDATE(id_bar, {'name': 'qux', 'city': 'tagtag'}), UPDATE(id_bar, {'name': 'qux', 'city': 'tagtag'}),
@ -99,7 +99,7 @@ class TestO2MSerialization(common.TransactionCase):
for name in ['NObar', 'baz', 'qux', 'NOquux', 'NOcorge', 'garply'] for name in ['NObar', 'baz', 'qux', 'NOquux', 'NOcorge', 'garply']
] ]
results = self.partner.resolve_o2m_commands_to_record_dicts( results = self.partner.resolve_2many_commands(
self.cr, UID, 'address', [ self.cr, UID, 'address', [
CREATE({'name': 'foo'}), CREATE({'name': 'foo'}),
UPDATE(ids[0], {'name': 'bar'}), UPDATE(ids[0], {'name': 'bar'}),
@ -131,7 +131,7 @@ class TestO2MSerialization(common.TransactionCase):
] ]
commands = map(lambda id: (4, id), ids) commands = map(lambda id: (4, id), ids)
results = self.partner.resolve_o2m_commands_to_record_dicts( results = self.partner.resolve_2many_commands(
self.cr, UID, 'address', commands, ['name']) self.cr, UID, 'address', commands, ['name'])
self.assertEqual(results, [ self.assertEqual(results, [
@ -144,7 +144,7 @@ class TestO2MSerialization(common.TransactionCase):
"DELETE_ALL can appear as a singleton" "DELETE_ALL can appear as a singleton"
try: try:
self.partner.resolve_o2m_commands_to_record_dicts( self.partner.resolve_2many_commands(
self.cr, UID, 'address', [(5,)], ['name']) self.cr, UID, 'address', [(5,)], ['name'])
except AssertionError: except AssertionError:
# 5 should fail with an assert error, but not e.g. a ValueError # 5 should fail with an assert error, but not e.g. a ValueError
@ -154,19 +154,19 @@ class TestO2MSerialization(common.TransactionCase):
"Commands with uncertain semantics in this context should be forbidden" "Commands with uncertain semantics in this context should be forbidden"
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
self.partner.resolve_o2m_commands_to_record_dicts( self.partner.resolve_2many_commands(
self.cr, UID, 'address', [DELETE(42)], ['name']) self.cr, UID, 'address', [DELETE(42)], ['name'])
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
self.partner.resolve_o2m_commands_to_record_dicts( self.partner.resolve_2many_commands(
self.cr, UID, 'address', [FORGET(42)], ['name']) self.cr, UID, 'address', [FORGET(42)], ['name'])
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
self.partner.resolve_o2m_commands_to_record_dicts( self.partner.resolve_2many_commands(
self.cr, UID, 'address', [DELETE_ALL()], ['name']) self.cr, UID, 'address', [DELETE_ALL()], ['name'])
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
self.partner.resolve_o2m_commands_to_record_dicts( self.partner.resolve_2many_commands(
self.cr, UID, 'address', [REPLACE_WITH([42])], ['name']) self.cr, UID, 'address', [REPLACE_WITH([42])], ['name'])