Skip to content

Commit 58bd009

Browse files
committed
[IMP] util/{models,pg}: check m2m tables on model rename
When renaming a model we need to check m2m tables that may need to be renamed as well. Otherwise the ORM will create a new table that would be empty. If the data is handled directly in the scripts the ignore parameter can be used to avoid warnings. Notes: * From Odoo 9 the column relation_table exists in ir_model_fields * From Odoo 10 the name of m2m tables is given by the model names ordered alphabetically Related: odoo/upgrade#7752
1 parent b977faa commit 58bd009

File tree

2 files changed

+77
-1
lines changed

2 files changed

+77
-1
lines changed

src/util/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
get_value_or_en_translation,
2929
parallel_execute,
3030
table_exists,
31+
update_m2m_tables,
3132
view_exists,
3233
)
3334

@@ -268,7 +269,7 @@ def _replace_model_in_computed_custom_fields(cr, source, target):
268269
)
269270

270271

271-
def rename_model(cr, old, new, rename_table=True):
272+
def rename_model(cr, old, new, rename_table=True, ignored_m2ms=()):
272273
"""
273274
Rename a model.
274275
@@ -285,6 +286,7 @@ def rename_model(cr, old, new, rename_table=True):
285286
new_table = table_of_model(cr, new)
286287
if new_table != old_table:
287288
pg_rename_table(cr, old_table, new_table)
289+
update_m2m_tables(cr, old_table, new_table, ignored_m2ms)
288290

289291
updates = [("wkf", "osv")] if table_exists(cr, "wkf") else []
290292
updates += [(ir.table, ir.res_model) for ir in indirect_references(cr) if ir.res_model]

src/util/pg.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,6 +1351,80 @@ def create_m2m(cr, m2m, fk1, fk2, col1=None, col2=None):
13511351
)
13521352

13531353

1354+
def update_m2m_tables(cr, old_table, new_table, ignored_m2ms=()):
1355+
"""
1356+
Update m2m table names and columns.
1357+
1358+
:param str from_model: model for which the underlying table changed
1359+
:param str old_table: former table of the model
1360+
:param str new_table: new table of the model
1361+
:param list(str) ignored_m2ms: explicit list of m2m tables to ignore
1362+
1363+
:meta private: exclude from online docs
1364+
"""
1365+
if old_table == new_table or not version_gte("10.0"):
1366+
return
1367+
ignored_m2ms = set(ignored_m2ms)
1368+
for orig_m2m_table in get_m2m_tables(cr, new_table):
1369+
if orig_m2m_table in ignored_m2ms:
1370+
continue
1371+
m = re.match(r"^(\w+)_{0}_rel|{0}_(\w+)_rel$".format(re.escape(old_table)), orig_m2m_table)
1372+
if m:
1373+
m2m_table = "{}_{}_rel".format(*sorted([m.group(1) or m.group(2), new_table]))
1374+
# Due to the 63 chars limit in generated constraint names, for long table names the FK
1375+
# constraint is dropped when renaming the table. We need the constraint to correctly
1376+
# identify the FK targets. The FK constraints will be dropped and recreated below.
1377+
rename_table(cr, orig_m2m_table, m2m_table, remove_constraints=False)
1378+
_logger.info("Renamed m2m table %s to %s", orig_m2m_table, m2m_table)
1379+
else:
1380+
m2m_table = orig_m2m_table
1381+
for m2m_col in get_columns(cr, m2m_table).iter_unquoted():
1382+
col_info = target_of(cr, m2m_table, m2m_col)
1383+
if not col_info or col_info[0] != new_table or col_info[1] != "id":
1384+
continue
1385+
old_col, new_col = map("{}_id".format, [old_table, new_table])
1386+
if m2m_col != old_col:
1387+
_logger.warning(
1388+
"Possibly missing rename: the column %s of m2m table %s references the table %s",
1389+
m2m_col,
1390+
m2m_table,
1391+
new_table,
1392+
)
1393+
continue
1394+
old_constraint = col_info[2]
1395+
cr.execute(
1396+
"""
1397+
SELECT c.confdeltype
1398+
FROM pg_constraint c
1399+
JOIN pg_class t
1400+
ON c.conrelid = t.oid
1401+
WHERE t.relname = %s
1402+
AND c.conname = %s
1403+
""",
1404+
[m2m_table, old_constraint],
1405+
)
1406+
on_delete = cr.fetchone()[0][0]
1407+
query = format_query(
1408+
cr,
1409+
"""
1410+
ALTER TABLE {m2m_table}
1411+
RENAME COLUMN {old_col} TO {new_col};
1412+
1413+
ALTER TABLE {m2m_table}
1414+
DROP CONSTRAINT {old_constraint},
1415+
ADD FOREIGN KEY ({new_col}) REFERENCES {new_table} (id) ON DELETE {del_action}
1416+
""",
1417+
m2m_table=m2m_table,
1418+
old_col=old_col,
1419+
new_col=new_col,
1420+
old_constraint=old_constraint,
1421+
new_table=new_table,
1422+
del_action=SQLStr("RESTRICT") if on_delete == "r" else SQLStr("CASCADE"),
1423+
)
1424+
cr.execute(query)
1425+
_logger.info("Renamed m2m column of table %s from %s to %s", m2m_table, old_col, new_col)
1426+
1427+
13541428
def fixup_m2m(cr, m2m, fk1, fk2, col1=None, col2=None):
13551429
if col1 is None:
13561430
col1 = "%s_id" % fk1

0 commit comments

Comments
 (0)