[IMP] core: setup o2m edition views recursively in server-side form

Before this commit, the SSF would only set up edition views on o2ms at
the toplevel. This usually isn't an issue, but in some cases involing
onchanges on o2ms which themselves contain o2m, odd results could
happen as the second level would try to list the sub-o2m's fields in
order to cleanup its (possible absence of) values (cf the `subfields`
local variable in _cleanup_onchange), which would blow up due to the
missing `"edition"` key.

Fix by recursively processing o2ms and setting up their edition view.

Also fix the fields iteration in fvg processing: don't recurse inside
fields. There still is no support for embedded views (I think?) but we
should not try to treat subview fields as view fields, that could have
very bad results.

closes odoo/odoo#28645
This commit is contained in:
Xavier Morel
2018-11-13 16:15:07 +00:00
parent 1e4b3d093e
commit 79728da9d3
+35 -37
View File
@@ -1097,42 +1097,6 @@ class Form(object):
arch = etree.fromstring(fvg['arch'])
object.__setattr__(self, '_view', fvg)
# TODO: make this less crappy?
# look up edition view for the O2M
for f, descr in fvg['fields'].items():
if descr['type'] != 'one2many':
continue
node = next(n for n in arch.iter('field') if n.get('name') == f)
default_view = next(
(m for m in node.get('mode', 'tree').split(',') if m != 'form'),
'tree'
)
refs = {
m.group('view_type'): m.group('view_id')
for m in ref_re.finditer(node.get('context', ''))
}
# always fetch for simplicity, ensure we always have a tree and
# a form view
submodel = env[descr['relation']]
views = submodel.with_context(**refs) \
.load_views([(False, 'tree'), (False, 'form')])['fields_views']
# embedded views should take the priority on externals
views.update(descr['views'])
# re-set all resolved views on the descriptor
descr['views'] = views
# if the default view is a kanban or a non-editable list, the
# "edition controller" is the form view
edition = views['form']
if default_view == 'tree':
subarch = etree.fromstring(views['tree']['arch'])
if subarch.get('editable'):
edition = views['tree']
self._process_fvg(submodel, edition)
descr['views']['edition'] = edition
self._process_fvg(recordp, fvg)
@@ -1149,6 +1113,35 @@ class Form(object):
else:
self._init_from_defaults(self._model)
def _o2m_set_edition_view(self, descr, node):
default_view = next(
(m for m in node.get('mode', 'tree').split(',') if m != 'form'),
'tree'
)
refs = {
m.group('view_type'): m.group('view_id')
for m in ref_re.finditer(node.get('context', ''))
}
# always fetch for simplicity, ensure we always have a tree and
# a form view
submodel = self._env[descr['relation']]
views = submodel.with_context(**refs) \
.load_views([(False, 'tree'), (False, 'form')])['fields_views']
# embedded views should take the priority on externals
views.update(descr['views'])
# re-set all resolved views on the descriptor
descr['views'] = views
# if the default view is a kanban or a non-editable list, the
# "edition controller" is the form view
edition = views['form']
if default_view == 'tree':
subarch = etree.fromstring(views['tree']['arch'])
if subarch.get('editable'):
edition = views['tree']
self._process_fvg(submodel, edition)
descr['views']['edition'] = edition
def __str__(self):
return "<%s %s(%s)>" % (
type(self).__name__,
@@ -1167,7 +1160,7 @@ class Form(object):
# pre-resolve modifiers & bind to arch toplevel
modifiers = fvg['modifiers'] = {}
contexts = fvg['contexts'] = {}
for f in etree.fromstring(fvg['arch']).iter('field'):
for f in etree.fromstring(fvg['arch']).xpath('//field[not(ancestor::field)]'):
fname = f.get('name')
modifiers[fname] = {
modifier: domain if isinstance(domain, bool) else normalize_domain(domain)
@@ -1176,6 +1169,11 @@ class Form(object):
ctx = f.get('context')
if ctx:
contexts[fname] = ctx
descr = fvg['fields'].get(fname) or {'type': None}
if descr['type'] == 'one2many':
self._o2m_set_edition_view(descr, f)
fvg['modifiers']['id'] = {'required': False, 'readonly': True}
fvg['onchange'] = model._onchange_spec(fvg)