16
16
17
17
import psycopg2
18
18
from psycopg2 import sql
19
- from psycopg2 .extras import Json , execute_values
19
+ from psycopg2 .extras import Json
20
20
21
21
try :
22
22
from odoo import release
@@ -40,7 +40,13 @@ def make_index_name(table_name, column_name):
40
40
from .const import ENVIRON
41
41
from .domains import _adapt_one_domain , _replace_path , _valid_path_to , adapt_domains
42
42
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
+ )
44
50
from .inherit import for_each_inherit
45
51
from .misc import SelfPrintEvalContext , log_progress , version_gte
46
52
from .orm import env , invalidate
@@ -79,126 +85,6 @@ def make_index_name(table_name, column_name):
79
85
)
80
86
81
87
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
-
145
- def rename_ir_exports_fields (cr , models_fields_map ):
146
- """
147
- Rename fields references in ir.exports.line records.
148
-
149
- :param dict[str, dict[str, str]] models_fields_map: a dict of models to the fields rename dict,
150
- like: `{"model.name": {"old_field": "new_field", ...}, ...}`
151
-
152
- :meta private: exclude from online docs
153
- """
154
- matching_exports = _get_resolved_ir_exports (
155
- cr ,
156
- fields = [(model , field ) for model , fields_map in models_fields_map .items () for field in fields_map ],
157
- )
158
- if not matching_exports :
159
- return
160
- _logger .debug ("Renaming %d export template lines with renamed fields" , len (matching_exports ))
161
- fixed_lines_paths = {}
162
- for line_id , resolved_paths in matching_exports .items ():
163
- for path_part in resolved_paths :
164
- assert path_part .field_model in models_fields_map
165
- fields_map = models_fields_map [path_part .field_model ]
166
- assert path_part .field_name in fields_map
167
- assert path_part .path [path_part .part_index - 1 ] == path_part .field_name
168
- new_field_name = fields_map [path_part .field_name ]
169
- fixed_path = fixed_lines_paths .get (line_id , list (path_part .path ))
170
- fixed_path [path_part .part_index - 1 ] = new_field_name
171
- fixed_lines_paths [line_id ] = fixed_path
172
- execute_values (
173
- cr ,
174
- """
175
- UPDATE ir_exports_line el
176
- SET name = v.name
177
- FROM (VALUES %s) AS v(id, name)
178
- WHERE el.id = v.id
179
- """ ,
180
- [(k , "/" .join (v )) for k , v in fixed_lines_paths .items ()],
181
- )
182
-
183
-
184
- def remove_ir_exports_lines (cr , models = None , fields = None ):
185
- """
186
- Delete ir.exports.line records that reference models or fields that are/will be removed.
187
-
188
- Only one of ``models`` or ``fields`` arguments should be provided.
189
-
190
- :param list[str] models: a list of model names to match in exports
191
- :param list[(str, str)] fields: a list of (model, field) tuples to match in exports
192
-
193
- :meta private: exclude from online docs
194
- """
195
- matching_exports = _get_resolved_ir_exports (cr , models = models , fields = fields )
196
- if not matching_exports :
197
- return
198
- _logger .debug ("Deleting %d export template lines with removed models/fields" , len (matching_exports ))
199
- cr .execute ("DELETE FROM ir_exports_line WHERE id IN %s" , [tuple (matching_exports .keys ())])
200
-
201
-
202
88
def ensure_m2o_func_field_data (cr , src_table , column , dst_table ):
203
89
"""
204
90
Fix broken m2o relations.
@@ -323,7 +209,7 @@ def clean_context(context):
323
209
)
324
210
325
211
# ir.exports.line
326
- remove_ir_exports_lines (cr , fields = [( model , fieldname )] )
212
+ _remove_export_lines (cr , model , fieldname )
327
213
328
214
def adapter (leaf , is_or , negated ):
329
215
# replace by TRUE_LEAF, unless negated or in a OR operation but not negated
@@ -1195,7 +1081,31 @@ def _update_field_usage_multi(cr, models, old, new, domain_adapter=None, skip_in
1195
1081
1196
1082
# ir.exports.line
1197
1083
if only_models :
1198
- rename_ir_exports_fields (cr , {model : {old : new } for model in only_models })
1084
+ cr .execute (
1085
+ """
1086
+ SELECT el.id,
1087
+ e.resource,
1088
+ STRING_TO_ARRAY(el.name, '/')
1089
+ FROM ir_exports_line el
1090
+ JOIN ir_exports e
1091
+ ON el.export_id = e.id
1092
+ WHERE el.name ~ %s
1093
+ """ ,
1094
+ [r"\y{}\y" .format (old )],
1095
+ )
1096
+ fixed_lines_paths = {}
1097
+ for line_id , line_model , line_path in cr .fetchall ():
1098
+ new_path = [
1099
+ new if x .field_name == old and x .field_model in only_models else x .field_name
1100
+ for x in resolve_model_fields_path (cr , line_model , line_path )
1101
+ ]
1102
+ if len (new_path ) == len (line_path ) and new_path != line_path :
1103
+ fixed_lines_paths [line_id ] = "/" .join (new_path )
1104
+ if fixed_lines_paths :
1105
+ cr .execute (
1106
+ "UPDATE ir_exports_line SET name = (%s::jsonb)->>(id::text) WHERE id IN %s" ,
1107
+ [Json (fixed_lines_paths ), tuple (fixed_lines_paths )],
1108
+ )
1199
1109
1200
1110
# mail.alias
1201
1111
if column_exists (cr , "mail_alias" , "alias_defaults" ):
0 commit comments