Skip to content

Commit e068fca

Browse files
committed
fixup! fixup! [ADD] util.fields: handle ir.exports model/fields renames/removal
1 parent 40fefc4 commit e068fca

File tree

3 files changed

+37
-88
lines changed

3 files changed

+37
-88
lines changed

src/util/fields.py

Lines changed: 11 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,13 @@ def make_index_name(table_name, column_name):
4040
from .const import ENVIRON
4141
from .domains import _adapt_one_domain, _replace_path, _valid_path_to, adapt_domains
4242
from .exceptions import SleepyDeveloperError
43-
from .helpers import _dashboard_actions, _validate_model, resolve_model_fields_path, table_of_model
43+
from .helpers import (
44+
_dashboard_actions,
45+
_remove_export_lines,
46+
_validate_model,
47+
resolve_model_fields_path,
48+
table_of_model,
49+
)
4450
from .inherit import for_each_inherit
4551
from .misc import SelfPrintEvalContext, log_progress, version_gte
4652
from .orm import env, invalidate
@@ -79,69 +85,6 @@ def make_index_name(table_name, column_name):
7985
)
8086

8187

82-
def _get_resolved_ir_exports(cr, models=None, fields=None):
83-
"""
84-
Return a list of ir.exports.line records which models or fields match the given arguments.
85-
86-
Export lines can reference nested models through relationship field "paths"
87-
(e.g. "partner_id/country_id/name"), therefore these needs to be resolved properly.
88-
89-
Only one of ``models`` or ``fields`` arguments should be provided.
90-
91-
:param list[str] models: a list of model names to match in exports
92-
:param list[(str, str)] fields: a list of (model, field) tuples to match in exports
93-
:return: the resolved field paths parts for each matched export line id
94-
:rtype: dict[int, list[FieldsPathPart]]
95-
96-
:meta private: exclude from online docs
97-
"""
98-
assert bool(models) ^ bool(fields), "One of models or fields must be given, and not both."
99-
100-
# Get the model fields paths for exports.
101-
# When matching fields we can already broadly filter on field names (will be double-checked later).
102-
# When matching models we can't exclude anything because we don't know intermediate models.
103-
where = ""
104-
params = {}
105-
if fields:
106-
fields = {(model, fields) for model, fields in fields} # noqa: C416 # make sure set[tuple]
107-
where = "WHERE el.name ~ ANY(%(field_names)s)"
108-
params["field_names"] = [f[1] for f in fields]
109-
cr.execute(
110-
"""
111-
SELECT el.id, e.resource AS model, string_to_array(el.name, '/') AS path
112-
FROM ir_exports e
113-
JOIN ir_exports_line el ON e.id = el.export_id
114-
{where}
115-
""".format(where=where),
116-
params,
117-
)
118-
paths_to_line_ids = {}
119-
for line_id, model, path in cr.fetchall():
120-
paths_to_line_ids.setdefault((model, tuple(path)), set()).add(line_id)
121-
122-
# Resolve intermediate models for all model fields paths, filter only matching paths parts
123-
matching_paths_parts = {}
124-
for model, path in paths_to_line_ids:
125-
resolved_paths = resolve_model_fields_path(cr, model, path)
126-
if fields:
127-
matching_parts = [p for p in resolved_paths if (p.field_model, p.field_name) in fields]
128-
else:
129-
matching_parts = [p for p in resolved_paths if p.field_model in models]
130-
if not matching_parts:
131-
continue
132-
matching_paths_parts[(model, path)] = matching_parts
133-
134-
# Return the matched parts for each export line id
135-
result = {}
136-
for (model, path), matching_parts in matching_paths_parts.items():
137-
line_ids = paths_to_line_ids.get((model, path))
138-
if not line_ids:
139-
continue # wut?
140-
for line_id in line_ids:
141-
result.setdefault(line_id, []).extend(matching_parts)
142-
return result
143-
144-
14588
def ensure_m2o_func_field_data(cr, src_table, column, dst_table):
14689
"""
14790
Fix broken m2o relations.
@@ -266,9 +209,7 @@ def clean_context(context):
266209
)
267210

268211
# ir.exports.line
269-
matching_exports = _get_resolved_ir_exports(cr, fields=[(model, fieldname)])
270-
if matching_exports:
271-
cr.execute("DELETE FROM ir_exports_line WHERE id IN %s", [tuple(matching_exports.keys())])
212+
_remove_export_lines(cr, model, fieldname)
272213

273214
def adapter(leaf, is_or, negated):
274215
# replace by TRUE_LEAF, unless negated or in a OR operation but not negated
@@ -1153,12 +1094,12 @@ def _update_field_usage_multi(cr, models, old, new, domain_adapter=None, skip_in
11531094
[r"\y{}\y".format(old)],
11541095
)
11551096
fixed_lines_paths = {}
1156-
for line_id, model, path in cr.fetchall():
1097+
for line_id, line_model, line_path in cr.fetchall():
11571098
new_path = [
11581099
new if x.field_name == old and x.field_model in only_models else x.field_name
1159-
for x in resolve_model_fields_path(cr, model, path)
1100+
for x in resolve_model_fields_path(cr, line_model, line_path)
11601101
]
1161-
if len(new_path) == len(path) and new_path != path:
1102+
if len(new_path) == len(line_path) and new_path != line_path:
11621103
fixed_lines_paths[line_id] = "/".join(new_path)
11631104
if fixed_lines_paths:
11641105
cr.execute(

src/util/helpers.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,3 +292,27 @@ def resolve_model_fields_path(cr, model, path):
292292
{"model": model, "path": list(path)},
293293
)
294294
return [FieldsPathPart(**row) for row in cr.dictfetchall()]
295+
296+
297+
def _remove_export_lines(cr, model, field=None):
298+
q = """
299+
SELECT el.id,
300+
e.resource,
301+
STRING_TO_ARRAY(el.name, '/')
302+
FROM ir_exports_line el
303+
JOIN ir_exports e
304+
ON el.export_id = e.id
305+
"""
306+
if field:
307+
q = cr.mogrify(q + " WHERE el.name ~ %s ", [r"\y{}\y".format(field)]).decode()
308+
cr.execute(q)
309+
to_rem = [
310+
line_id
311+
for line_id, line_model, line_path in cr.fetchall()
312+
if any(
313+
x.field_model == model and (field is None or x.field_name == field)
314+
for x in resolve_model_fields_path(cr, line_model, line_path)
315+
)
316+
]
317+
if to_rem:
318+
cr.execute("DELETE FROM ir_exports_line WHERE id IN %s", [tuple(to_rem)])

src/util/models.py

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from .const import ENVIRON
1313
from .fields import IMD_FIELD_PATTERN, remove_field
14-
from .helpers import _ir_values_value, _validate_model, model_of_table, resolve_model_fields_path, table_of_model
14+
from .helpers import _ir_values_value, _remove_export_lines, _validate_model, model_of_table, table_of_model
1515
from .indirect_references import indirect_references
1616
from .inherit import for_each_inherit, inherit_parents
1717
from .misc import _cached, chunks, log_progress
@@ -130,23 +130,7 @@ def remove_model(cr, model, drop_table=True, ignore_m2m=()):
130130
notify = notify or bool(cr.rowcount)
131131

132132
# for ir.exports.line we have to take care of "nested" references in fields "paths"
133-
cr.execute(
134-
"""
135-
SELECT el.id,
136-
e.resource AS model,
137-
string_to_array(el.name, '/') AS path
138-
FROM ir_exports e
139-
JOIN ir_exports_line el
140-
ON e.id = el.export_id
141-
""",
142-
)
143-
to_rem = [
144-
line_id
145-
for line_id, line_model, line_path in cr.fetchall()
146-
if any(x.field_model == model for x in resolve_model_fields_path(cr, line_model, line_path))
147-
]
148-
if to_rem:
149-
cr.execute("DELETE FROM ir_exports_line WHERE id IN %s", [tuple(to_rem)])
133+
_remove_export_lines(cr, model)
150134

151135
_rm_refs(cr, model)
152136

0 commit comments

Comments
 (0)