Skip to content

Commit edc0489

Browse files
committed
[IMP] util/models: 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.
1 parent 4d8a67f commit edc0489

File tree

1 file changed

+67
-1
lines changed

1 file changed

+67
-1
lines changed

src/util/models.py

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@
2424
explode_execute,
2525
explode_query_range,
2626
format_query,
27+
get_columns,
2728
get_m2m_tables,
2829
get_value_or_en_translation,
2930
parallel_execute,
3031
table_exists,
32+
target_of,
3133
view_exists,
3234
)
3335

@@ -268,7 +270,7 @@ def _replace_model_in_computed_custom_fields(cr, source, target):
268270
)
269271

270272

271-
def rename_model(cr, old, new, rename_table=True):
273+
def rename_model(cr, old, new, rename_table=True, ignore_m2m=()):
272274
"""
273275
Rename a model.
274276
@@ -285,6 +287,70 @@ def rename_model(cr, old, new, rename_table=True):
285287
new_table = table_of_model(cr, new)
286288
if new_table != old_table:
287289
pg_rename_table(cr, old_table, new_table)
290+
m2m_tables = [t for t in get_m2m_tables(cr, new_table) if t not in ignore_m2m]
291+
cr.execute(
292+
"""
293+
SELECT relation_table,
294+
ARRAY_AGG(id)
295+
FROM ir_model_fields
296+
WHERE ttype = 'many2many'
297+
AND relation_table = ANY(%s)
298+
AND ( model = %s
299+
OR relation = %s)
300+
GROUP BY relation_table
301+
ORDER BY relation_table
302+
""",
303+
[m2m_tables, old, old],
304+
)
305+
for m2m_table, field_ids in cr.fetchall():
306+
if not table_exists(cr, m2m_table):
307+
# Possibly handled in the upgrade scripts already
308+
continue
309+
new_m2m_table = m2m_table
310+
m = re.match(r"(\w+)_{}_rel".format(old_table), m2m_table) or re.match(
311+
r"{}_(\w+)_rel".format(old_table), m2m_table
312+
)
313+
if m:
314+
new_m2m_table = "{}_{}_rel".format(*sorted([m.group(1), new_table]))
315+
# For long table names the FK constraint is dropped when renaming the table
316+
# We need the constraint to correctly identify the FK targets
317+
# They will be dropped and recreated anyway here below
318+
pg_rename_table(cr, m2m_table, new_m2m_table, remove_constraints=False)
319+
_logger.info("Renamed m2m table %s to %s", m2m_table, new_m2m_table)
320+
cr.execute(
321+
"UPDATE ir_model_fields SET relation_table = %s WHERE id IN %s",
322+
[new_m2m_table, tuple(field_ids)],
323+
)
324+
for m2m_col in get_columns(cr, new_m2m_table).iter_unquoted():
325+
col_info = target_of(cr, new_m2m_table, m2m_col)
326+
if col_info and col_info[0] == new_table and col_info[1] == "id":
327+
old_col, new_col = map("{}_id".format, [old_table, new_table])
328+
if m2m_col == old_col:
329+
query = format_query(
330+
cr,
331+
"""
332+
ALTER TABLE {m2m_table}
333+
RENAME COLUMN {old_col} TO {new_col};
334+
335+
ALTER TABLE {m2m_table}
336+
DROP CONSTRAINT {old_constraint_name},
337+
ADD FOREIGN KEY ({new_col}) REFERENCES {new_table} (id)
338+
""",
339+
m2m_table=new_m2m_table,
340+
old_col=old_col,
341+
new_col=new_col,
342+
old_constraint_name=col_info[2],
343+
new_table=new_table,
344+
)
345+
cr.execute(query)
346+
_logger.info("Renamed m2m column of table %s from %s to %s", m2m_table, old_col, new_col)
347+
else:
348+
_logger.warning(
349+
"Possibly missing rename: the column %s of m2m table %s references the table %s",
350+
m2m_col,
351+
m2m_table,
352+
new_table,
353+
)
288354

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

0 commit comments

Comments
 (0)