[IMP] core: add force_save support to server-side form
Cf 3b3f6f04af for basic feature
description.
Task 1931580
closes odoo/odoo#30428
This commit is contained in:
@@ -101,6 +101,8 @@
|
||||
<field name="f2" attrs="{'readonly': [['f1', '=', 63], ['f3', '=', 5]]}"/>
|
||||
<field name="f3"/>
|
||||
<field name="f4"/>
|
||||
<field name="f5" readonly="1"/>
|
||||
<field name="f6" readonly="1" force_save="1"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -10,10 +10,14 @@ class A(models.Model):
|
||||
f2 = fields.Integer(default=42)
|
||||
f3 = fields.Integer()
|
||||
f4 = fields.Integer(compute='_compute_f4')
|
||||
f5 = fields.Integer()
|
||||
f6 = fields.Integer()
|
||||
|
||||
@api.onchange('f2')
|
||||
def _on_change_f2(self):
|
||||
self.f3 = int(self.f2 / 2)
|
||||
self.f5 = self.f2
|
||||
self.f6 = self.f2
|
||||
|
||||
@api.depends('f1', 'f2')
|
||||
def _compute_f4(self):
|
||||
|
||||
@@ -65,6 +65,19 @@ class TestBasic(TransactionCase):
|
||||
with self.assertRaises(AssertionError):
|
||||
f.f2 = 42
|
||||
|
||||
def test_readonly_save(self):
|
||||
""" Should not save readonly fields unless they're force_save
|
||||
"""
|
||||
f = Form(self.env['test_testing_utilities.a'], view='test_testing_utilities.non_normalized_attrs')
|
||||
|
||||
f.f1 = 1
|
||||
f.f2 = 987
|
||||
self.assertEqual(f.f5, 987)
|
||||
self.assertEqual(f.f6, 987)
|
||||
r = f.save()
|
||||
self.assertEqual(r.f5, 0)
|
||||
self.assertEqual(r.f6, 987)
|
||||
|
||||
def test_attrs(self):
|
||||
""" Checks that attrs/modifiers with non-normalized domains work
|
||||
"""
|
||||
|
||||
+17
-4
@@ -1098,7 +1098,7 @@ class Form(object):
|
||||
else:
|
||||
view_id = view or False
|
||||
fvg = recordp.fields_view_get(view_id, 'form')
|
||||
arch = etree.fromstring(fvg['arch'])
|
||||
fvg['tree'] = etree.fromstring(fvg['arch'])
|
||||
|
||||
object.__setattr__(self, '_view', fvg)
|
||||
# TODO: make this less crappy?
|
||||
@@ -1107,7 +1107,7 @@ class Form(object):
|
||||
if descr['type'] != 'one2many':
|
||||
continue
|
||||
|
||||
node = next(n for n in arch.iter('field') if n.get('name') == f)
|
||||
node = self._get_node(f)
|
||||
default_view = next(
|
||||
(m for m in node.get('mode', 'tree').split(',') if m != 'form'),
|
||||
'tree'
|
||||
@@ -1130,10 +1130,12 @@ class Form(object):
|
||||
# if the default view is a kanban or a non-editable list, the
|
||||
# "edition controller" is the form view
|
||||
edition = views['form']
|
||||
edition['tree'] = etree.fromstring(edition['arch'])
|
||||
if default_view == 'tree':
|
||||
subarch = etree.fromstring(views['tree']['arch'])
|
||||
if subarch.get('editable'):
|
||||
edition = views['tree']
|
||||
edition['tree'] = subarch
|
||||
|
||||
self._process_fvg(submodel, edition)
|
||||
descr['views']['edition'] = edition
|
||||
@@ -1153,6 +1155,14 @@ class Form(object):
|
||||
else:
|
||||
self._init_from_defaults(self._model)
|
||||
|
||||
def _get_node(self, f):
|
||||
""" Find etree node for the field ``f`` in the current arch
|
||||
"""
|
||||
return next(
|
||||
n for n in self._view['tree'].iter('field')
|
||||
if n.get('name') == f
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "<%s %s(%s)>" % (
|
||||
type(self).__name__,
|
||||
@@ -1171,7 +1181,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 fvg['tree'].iter('field'):
|
||||
fname = f.get('name')
|
||||
modifiers[fname] = {
|
||||
modifier: domain if isinstance(domain, bool) else normalize_domain(domain)
|
||||
@@ -1356,8 +1366,11 @@ class Form(object):
|
||||
# skip unmodified fields
|
||||
if f not in self._changed:
|
||||
continue
|
||||
|
||||
if self._get_modifier(f, 'readonly'):
|
||||
continue
|
||||
node = self._get_node(f)
|
||||
if not node.get('force_save'):
|
||||
continue
|
||||
# TODO: filter out (1, _, {}) from o2m values
|
||||
values[f] = v
|
||||
return values
|
||||
|
||||
Reference in New Issue
Block a user