Skip to content

Commit 8f50e28

Browse files
committed
[FIX] util/fields: handle o2m when removing relation field
`relation_field_id` is an ON DELETE CASCADE FK. This triggers the removal of entries in `ir_model_fields` when we remove a field referenced by an o2m field. This action leaves many (metadata) leftovers for manual fields. To avoid potential issues we call `remove_field` on such fields. That way the full cleanup (domains, contexts, filters, etc.) happens. We also now warn about compute implementation of manual fields if we detect that the implementation may refer to a field being removed from the depends. closes #321 Signed-off-by: Christophe Simonis (chs) <[email protected]>
1 parent 85c5a6c commit 8f50e28

File tree

1 file changed

+46
-16
lines changed

1 file changed

+46
-16
lines changed

src/util/fields.py

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -278,17 +278,27 @@ def adapter(leaf, is_or, negated):
278278
# remove this field from dependencies of other fields
279279
if column_exists(cr, "ir_model_fields", "depends"):
280280
cr.execute(
281-
"SELECT id,model,depends FROM ir_model_fields WHERE state='manual' AND depends ~ %s",
281+
"SELECT id,model,depends,COALESCE(compute,''),name FROM ir_model_fields WHERE state='manual' AND depends ~ %s",
282282
[r"\m{}\M".format(fieldname)],
283283
)
284-
for id, from_model, deps in cr.fetchall():
284+
for id, from_model, deps, compute_code, dep_field_name in cr.fetchall():
285285
parts = []
286286
for part in deps.split(","):
287287
path = part.strip().split(".")
288288
if not any(
289289
path[i] == fieldname and _valid_path_to(cr, path[:i], from_model, model) for i in range(len(path))
290290
):
291291
parts.append(part)
292+
elif fieldname in compute_code:
293+
_logger.warning(
294+
"Field %s.%s depends on removed field %s.%s and its compute code references it, "
295+
"this may lead to errors.",
296+
from_model,
297+
dep_field_name,
298+
model,
299+
fieldname,
300+
)
301+
292302
if len(parts) != len(deps.split(",")):
293303
cr.execute("UPDATE ir_model_fields SET depends=%s WHERE id=%s", [", ".join(parts) or None, id])
294304

@@ -319,20 +329,6 @@ def adapter(leaf, is_or, negated):
319329
)
320330
ENVIRON.setdefault("_gone_m2m", {})[m2m_rel] = "%s:%s" % (model, fieldname)
321331

322-
# remove the ir.model.fields entry (and its xmlid)
323-
cr.execute(
324-
"""
325-
WITH del AS (
326-
DELETE FROM ir_model_fields WHERE model=%s AND name=%s RETURNING id
327-
)
328-
DELETE FROM ir_model_data
329-
USING del
330-
WHERE model = 'ir.model.fields'
331-
AND res_id = del.id
332-
""",
333-
[model, fieldname],
334-
)
335-
336332
# cleanup translations
337333
if table_exists(cr, "ir_translation"):
338334
cr.execute(
@@ -393,6 +389,40 @@ def adapter(leaf, is_or, negated):
393389
if drop_column and stored:
394390
remove_column(cr, table, fieldname, cascade=cascade)
395391

392+
# relation_field_id is a FK with ON DELETE CASCADE
393+
if column_exists(cr, "ir_model_fields", "relation_field_id"):
394+
cr.execute(
395+
"""
396+
SELECT d.model,
397+
d.name
398+
FROM ir_model_fields d
399+
JOIN ir_model_fields f
400+
ON d.relation_field_id = f.id
401+
WHERE f.model = %s
402+
AND f.name = %s
403+
AND d.ttype = 'one2many'
404+
AND d.state = 'manual'
405+
""",
406+
[model, fieldname],
407+
)
408+
for rel_model, rel_field in cr.fetchall():
409+
_logger.info("Cascade removing one2many field %s.%s", rel_model, rel_field)
410+
remove_field(cr, rel_model, rel_field)
411+
412+
# remove the ir.model.fields entry (and its xmlid)
413+
cr.execute(
414+
"""
415+
WITH del AS (
416+
DELETE FROM ir_model_fields WHERE model=%s AND name=%s RETURNING id
417+
)
418+
DELETE FROM ir_model_data
419+
USING del
420+
WHERE model = 'ir.model.fields'
421+
AND res_id = del.id
422+
""",
423+
[model, fieldname],
424+
)
425+
396426
# remove field on inherits
397427
for inh in for_each_inherit(cr, model, skip_inherit):
398428
remove_field(

0 commit comments

Comments
 (0)