[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
This commit is contained in:
@@ -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):
|
||||
|
||||
+21
-3
@@ -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')
|
||||
|
||||
+12
-39
@@ -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']
|
||||
|
||||
Reference in New Issue
Block a user