[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:
+35
-37
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user