summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphael Collet <rco@odoo.com>2020-06-09 12:08:09 +0000
committerRaphael Collet <rco@odoo.com>2020-08-20 13:39:04 +0000
commit488e334fc8fd6601e304635fa577c98b9bfd18e6 (patch)
tree30c70f257a17ccf36a71ffa66dd917ddf9ef978e
parent540682b9bef4d3ee1c02056aa5199b5b463a9363 (diff)
[REF] core: combine default_get() with first onchange()
When creating a new record, the client calls `default_get()`, completes the returned values with `False`, and calls `onchange()` to apply the onchange to the defaults. Optimize the double round-trip by integrating `default_get()` inside `onchange()` for the first call. The method is called with an empty list of fields, and usually no field values, except for records in a one2many field. In this case, `onchange()` does both steps above. Task 2261084
-rw-r--r--odoo/addons/test_testing_utilities/tests/test_form_impl.py4
-rw-r--r--odoo/models.py24
-rw-r--r--odoo/tests/common.py51
3 files changed, 35 insertions, 44 deletions
diff --git a/odoo/addons/test_testing_utilities/tests/test_form_impl.py b/odoo/addons/test_testing_utilities/tests/test_form_impl.py
index af49d27cbe5..ab846bda87e 100644
--- a/odoo/addons/test_testing_utilities/tests/test_form_impl.py
+++ b/odoo/addons/test_testing_utilities/tests/test_form_impl.py
@@ -374,13 +374,13 @@ class TestO2M(TransactionCase):
with f.subs.edit(index=0) as s:
self.assertEqual(s.v, 5)
- self.assertEqual(s.value, False)
+ self.assertEqual(s.value, 2)
r = f.save()
self.assertEqual(
[get(s) for s in r.subs],
- [("5", 0, 5)]
+ [("5", 2, 5)]
)
def test_o2m_inner_default(self):
diff --git a/odoo/models.py b/odoo/models.py
index d82806b6862..f3a2b4d7530 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -5857,6 +5857,10 @@ Fields:
names (in view order), or False
:param field_onchange: dictionary mapping field names to their
on_change attribute
+
+ When ``field_name`` is falsy, the method first adds default values
+ to ``values``, applies onchange methods to them, and return all the
+ fields in ``field_onchange``.
"""
# this is for tests using `Form`
self.flush()
@@ -5869,6 +5873,8 @@ Fields:
else:
names = []
+ first_call = not names
+
if not all(name in self._fields for name in names):
return {}
@@ -5931,14 +5937,16 @@ Fields:
)
)
- def diff(self, other):
+ def diff(self, other, force=False):
""" Return the values in ``self`` that differ from ``other``.
Requires record cache invalidation for correct output!
"""
record = self['<record>']
result = {}
for name, subnames in self['<tree>'].items():
- if (name == 'id') or (other.get(name) == self[name]):
+ if name == 'id':
+ continue
+ if not force and other.get(name) == self[name]:
continue
field = record._fields[name]
if field.type not in ('one2many', 'many2many'):
@@ -5968,6 +5976,16 @@ Fields:
nametree = PrefixTree(self.browse(), field_onchange)
+ if first_call:
+ names = list(nametree)
+ values.update(self.default_get([
+ name
+ for name in names
+ if name not in values
+ ]))
+ for name in names:
+ values.setdefault(name, False)
+
# prefetch x2many lines without data (for the initial snapshot)
for name, subnames in nametree.items():
if subnames and values.get(name):
@@ -6065,7 +6083,7 @@ Fields:
# determine values that have changed by comparing snapshots
self.invalidate_cache()
- result['value'] = snapshot1.diff(snapshot0)
+ result['value'] = snapshot1.diff(snapshot0, force=first_call)
# format warnings
warnings = result.pop('warnings')
diff --git a/odoo/tests/common.py b/odoo/tests/common.py
index 066bb067148..106cde6f74e 100644
--- a/odoo/tests/common.py
+++ b/odoo/tests/common.py
@@ -1695,42 +1695,12 @@ class Form(object):
def _init_from_defaults(self, model):
vals = self._values
- fields = self._view['fields']
- def cleanup(k, v):
- if fields[k]['type'] == 'one2many':
- return [
- # use None as "empty" value for UPDATE instead of {}
- (1, c[1], None) if c[0] == 1 and not c[2] else c
- for c in v
- if c[0] != 6 # o2m default gets a (6) at the start, nonsensical
- ]
- elif fields[k]['type'] == 'datetime' and isinstance(v, datetime):
- return odoo.fields.Datetime.to_string(v)
- elif fields[k]['type'] == 'date' and isinstance(v, date):
- return odoo.fields.Datetime.to_string(v)
+ vals.clear()
+ vals['id'] = False
- return v
- defaults = {
- k: cleanup(k, v)
- for k, v in model.default_get(list(fields)).items()
- if k in fields
- }
- vals.update(defaults)
- # m2m should all be rep'd as command list
- for k, v in vals.items():
- if not v:
- type_ = fields[k]['type']
- if type_ == 'many2many':
- vals[k] = [(6, False, [])]
- elif type_ == 'one2many':
- vals[k] = []
- elif type_ in ('integer', 'float'):
- vals[k] = 0
-
- # on creation, every field is considered changed by the client
- # apparently
- # and fields should be sent in view order, not whatever fields_view_get['fields'].keys() is
- self._perform_onchange(self._view['fields_ordered'])
+ # call onchange with an empty list of fields; this retrieves default
+ # values, applies onchanges and return the result
+ self._perform_onchange([])
def _init_from_values(self, values):
self._values.update(
@@ -1929,6 +1899,9 @@ class Form(object):
"""
values = {}
for f in fields:
+ if f == 'id':
+ continue
+
get_modifier = functools.partial(
self._get_modifier,
f, modmap=view['modifiers'],
@@ -1941,8 +1914,8 @@ class Form(object):
if get_modifier('required'):
raise AssertionError("{} is a required field ({})".format(f, view['modifiers'][f]))
- # skip unmodified fields unless all_fields (also always ignore id)
- if f == 'id' or not (all_fields or f in changed):
+ # skip unmodified fields unless all_fields
+ if not (all_fields or f in changed):
continue
if get_modifier('readonly'):
@@ -1997,7 +1970,7 @@ class Form(object):
# skip calling onchange() if there's no trigger on any of the changed
# fields
spec = self._view['onchange']
- if not any(spec[f] for f in fields):
+ if fields and not any(spec[f] for f in fields):
return
record = self._model.browse(self._values.get('id'))
@@ -2012,7 +1985,7 @@ class Form(object):
self._values.update(
(k, self._cleanup_onchange(
self._view['fields'][k],
- v, self._values[k],
+ v, self._values.get(k),
))
for k, v in values.items()
if k in self._view['fields']