Skip to content

Commit d7ac8fd

Browse files
committed
[REF] util.views: create new package, move views-related code
Changes: - move `util.views_convert` -> `util.views.convert` - move records-related views helpers into `util.views.records` - move `util.convert_bootstrap` -> `util.views.bootstrap` - move some helpers from `util.records` -> `util.views.records` - add version checks for views/html conversion helpers
1 parent 6d79820 commit d7ac8fd

File tree

10 files changed

+867
-808
lines changed

10 files changed

+867
-808
lines changed

src/util/convert_bootstrap.py

Lines changed: 7 additions & 460 deletions
Large diffs are not rendered by default.

src/util/domains.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from .inherit import for_each_inherit
3838
from .misc import SelfPrintEvalContext
3939
from .pg import column_exists, get_value_or_en_translation, table_exists
40-
from .records import edit_view
40+
from .views.records import edit_view
4141

4242
# python3 shims
4343
try:

src/util/models.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@
3131

3232
# avoid namespace clash
3333
from .pg import rename_table as pg_rename_table
34-
from .records import _rm_refs, remove_records, remove_view, replace_record_references_batch
34+
from .records import _rm_refs, remove_records, replace_record_references_batch
3535
from .report import add_to_migration_reports
36+
from .views.records import remove_view
3637

3738
_logger = logging.getLogger(__name__)
3839

src/util/modules.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
from .models import delete_model
4949
from .orm import env, flush
5050
from .pg import column_exists, table_exists, target_of
51-
from .records import ref, remove_menus, remove_records, remove_view, replace_record_references_batch
51+
from .records import ref, remove_menus, remove_records, replace_record_references_batch
52+
from .views.records import remove_view
5253

5354
INSTALLED_MODULE_STATES = ("installed", "to install", "to upgrade")
5455
NO_AUTOINSTALL = str2bool(os.getenv("UPG_NO_AUTOINSTALL", "0")) if version_gte("15.0") else False

src/util/records.py

Lines changed: 1 addition & 246 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import logging
55
import os
66
import re
7-
from contextlib import contextmanager
87
from operator import itemgetter
98

109
import lxml
@@ -14,7 +13,6 @@
1413
from odoo import release
1514
from odoo.tools.convert import xml_import
1615
from odoo.tools.misc import file_open
17-
from odoo.tools.translate import xml_translate
1816
except ImportError:
1917
from openerp import release
2018
from openerp.tools.convert import xml_import
@@ -44,7 +42,7 @@
4442
table_exists,
4543
target_of,
4644
)
47-
from .report import add_to_migration_reports
45+
from .views.records import add_view, edit_view, remove_view # noqa: F401
4846

4947
_logger = logging.getLogger(__name__)
5048

@@ -55,249 +53,6 @@
5553
basestring = unicode = str
5654

5755

58-
def remove_view(cr, xml_id=None, view_id=None, silent=False, key=None):
59-
"""
60-
Remove a view and all its descendants.
61-
62-
This function recursively deletes the given view and its inherited views, as long as
63-
they are part of a module. It will fail as soon as a custom view exists anywhere in
64-
the hierarchy. It also removes multi-website COWed views.
65-
66-
:param str xml_id: optional, the xml_id of the view to remove
67-
:param int view_id: optional, the ID of the view to remove
68-
:param bool silent: whether to show in the logs disabled custom views
69-
:param str or None key: key used to detect multi-website COWed views, if `None` then
70-
set to `xml_id` if provided, otherwise set to the xml_id
71-
referencing the view with ID `view_id` if any
72-
73-
.. warning::
74-
Either `xml_id` or `view_id` must be set. Specifying both will raise an error.
75-
"""
76-
assert bool(xml_id) ^ bool(view_id)
77-
if xml_id:
78-
view_id = ref(cr, xml_id)
79-
if view_id:
80-
module, _, name = xml_id.partition(".")
81-
cr.execute("SELECT model FROM ir_model_data WHERE module=%s AND name=%s", [module, name])
82-
83-
[model] = cr.fetchone()
84-
if model != "ir.ui.view":
85-
raise ValueError("%r should point to a 'ir.ui.view', not a %r" % (xml_id, model))
86-
else:
87-
# search matching xmlid for logging or renaming of custom views
88-
xml_id = "?"
89-
if not key:
90-
cr.execute("SELECT module, name FROM ir_model_data WHERE model='ir.ui.view' AND res_id=%s", [view_id])
91-
if cr.rowcount:
92-
xml_id = "%s.%s" % cr.fetchone()
93-
94-
# From given or determined xml_id, the views duplicated in a multi-website
95-
# context are to be found and removed.
96-
if xml_id != "?" and column_exists(cr, "ir_ui_view", "key"):
97-
cr.execute("SELECT id FROM ir_ui_view WHERE key = %s AND id != %s", [xml_id, view_id])
98-
for [v_id] in cr.fetchall():
99-
remove_view(cr, view_id=v_id, silent=silent, key=xml_id)
100-
101-
if not view_id:
102-
return
103-
104-
cr.execute(
105-
"""
106-
SELECT v.id, x.module || '.' || x.name, v.name
107-
FROM ir_ui_view v LEFT JOIN
108-
ir_model_data x ON (v.id = x.res_id AND x.model = 'ir.ui.view' AND x.module !~ '^_')
109-
WHERE v.inherit_id = %s;
110-
""",
111-
[view_id],
112-
)
113-
for child_id, child_xml_id, child_name in cr.fetchall():
114-
if child_xml_id:
115-
if not silent:
116-
_logger.info(
117-
"remove deprecated built-in view %s (ID %s) as parent view %s (ID %s) is going to be removed",
118-
child_xml_id,
119-
child_id,
120-
xml_id,
121-
view_id,
122-
)
123-
remove_view(cr, child_xml_id, silent=True)
124-
else:
125-
if not silent:
126-
_logger.warning(
127-
"deactivate deprecated custom view with ID %s as parent view %s (ID %s) is going to be removed",
128-
child_id,
129-
xml_id,
130-
view_id,
131-
)
132-
disable_view_query = """
133-
UPDATE ir_ui_view
134-
SET name = (name || ' - old view, inherited from ' || %%s),
135-
inherit_id = NULL
136-
%s
137-
WHERE id = %%s
138-
"""
139-
# In 8.0, disabling requires setting mode to 'primary'
140-
extra_set_sql = ""
141-
if column_exists(cr, "ir_ui_view", "mode"):
142-
extra_set_sql = ", mode = 'primary' "
143-
144-
# Column was not present in v7 and it's older version
145-
if column_exists(cr, "ir_ui_view", "active"):
146-
extra_set_sql += ", active = false "
147-
148-
disable_view_query = disable_view_query % extra_set_sql
149-
cr.execute(disable_view_query, (key or xml_id, child_id))
150-
add_to_migration_reports(
151-
{"id": child_id, "name": child_name},
152-
"Disabled views",
153-
)
154-
if not silent:
155-
_logger.info("remove deprecated %s view %s (ID %s)", key and "COWed" or "built-in", key or xml_id, view_id)
156-
157-
remove_records(cr, "ir.ui.view", [view_id])
158-
159-
160-
@contextmanager
161-
def edit_view(cr, xmlid=None, view_id=None, skip_if_not_noupdate=True, active=True):
162-
"""
163-
Context manager to edit a view's arch.
164-
165-
This function returns a context manager that may yield a parsed arch of a view as an
166-
`etree Element <https://lxml.de/tutorial.html#the-element-class>`_. Any changes done
167-
in the returned object will be written back to the database upon exit of the context
168-
manager, updating also the translated versions of the arch. Since the function may not
169-
yield, use :func:`~odoo.upgrade.util.misc.skippable_cm` to avoid errors.
170-
171-
.. code-block:: python
172-
173-
with util.skippable_cm(), util.edit_view(cr, "xml.id") as arch:
174-
arch.attrib["string"] = "My Form"
175-
176-
To select the target view to edit use either `xmlid` or `view_id`, not both.
177-
178-
When the view is identified by `view_id`, the arch is always yielded if the view
179-
exists, with disregard to any `noupdate` flag it may have associated. When `xmlid` is
180-
set, if the view `noupdate` flag is `True` then the arch will not be yielded *unless*
181-
`skip_if_not_noupdate` is set to `False`. If `noupdate` is `False`, the view will be
182-
yielded for edit.
183-
184-
If the `active` argument is not `None`, the `active` flag of the view will be set
185-
accordingly.
186-
187-
.. warning::
188-
The default value of `active` is `True`, therefore views are always *activated* by
189-
default. To avoid inadvertently activating views, pass `None` as `active` parameter.
190-
191-
:param str xmlid: optional, xml_id of the view edit
192-
:param int view_id: optional, ID of the view to edit
193-
:param bool skip_if_not_noupdate: whether to force the edit of views requested via
194-
`xmlid` parameter even if they are flagged as
195-
`noupdate=True`, ignored if `view_id` is set
196-
:param bool or None active: active flag value to set, nothing is set when `None`
197-
:return: a context manager that yields the parsed arch, upon exit the context manager
198-
writes back the changes.
199-
"""
200-
assert bool(xmlid) ^ bool(view_id), "You Must specify either xmlid or view_id"
201-
noupdate = True
202-
if xmlid:
203-
if "." not in xmlid:
204-
raise ValueError("Please use fully qualified name <module>.<name>")
205-
206-
module, _, name = xmlid.partition(".")
207-
cr.execute(
208-
"""
209-
SELECT res_id, noupdate
210-
FROM ir_model_data
211-
WHERE module = %s
212-
AND name = %s
213-
""",
214-
[module, name],
215-
)
216-
data = cr.fetchone()
217-
if data:
218-
view_id, noupdate = data
219-
220-
if view_id and not (skip_if_not_noupdate and not noupdate):
221-
arch_col = "arch_db" if column_exists(cr, "ir_ui_view", "arch_db") else "arch"
222-
jsonb_column = column_type(cr, "ir_ui_view", arch_col) == "jsonb"
223-
cr.execute(
224-
"""
225-
SELECT {arch}
226-
FROM ir_ui_view
227-
WHERE id=%s
228-
""".format(
229-
arch=arch_col,
230-
),
231-
[view_id],
232-
)
233-
[arch] = cr.fetchone() or [None]
234-
if arch:
235-
236-
def parse(arch):
237-
arch = arch.encode("utf-8") if isinstance(arch, unicode) else arch
238-
return lxml.etree.fromstring(arch.replace(b"&#13;\n", b"\n").strip())
239-
240-
if jsonb_column:
241-
242-
def get_trans_terms(value):
243-
terms = []
244-
xml_translate(terms.append, value)
245-
return terms
246-
247-
translation_terms = {lang: get_trans_terms(value) for lang, value in arch.items()}
248-
arch_etree = parse(arch["en_US"])
249-
yield arch_etree
250-
new_arch = lxml.etree.tostring(arch_etree, encoding="unicode")
251-
terms_en = translation_terms["en_US"]
252-
arch_column_value = Json(
253-
{
254-
lang: xml_translate(dict(zip(terms_en, terms)).get, new_arch)
255-
for lang, terms in translation_terms.items()
256-
}
257-
)
258-
else:
259-
arch_etree = parse(arch)
260-
yield arch_etree
261-
arch_column_value = lxml.etree.tostring(arch_etree, encoding="unicode")
262-
263-
set_active = ", active={}".format(bool(active)) if active is not None else ""
264-
cr.execute(
265-
"UPDATE ir_ui_view SET {arch}=%s{set_active} WHERE id=%s".format(arch=arch_col, set_active=set_active),
266-
[arch_column_value, view_id],
267-
)
268-
269-
270-
def add_view(cr, name, model, view_type, arch_db, inherit_xml_id=None, priority=16):
271-
inherit_id = None
272-
if inherit_xml_id:
273-
inherit_id = ref(cr, inherit_xml_id)
274-
if not inherit_id:
275-
raise ValueError(
276-
"Unable to add view '%s' because its inherited view '%s' cannot be found!" % (name, inherit_xml_id)
277-
)
278-
arch_col = "arch_db" if column_exists(cr, "ir_ui_view", "arch_db") else "arch"
279-
jsonb_column = column_type(cr, "ir_ui_view", arch_col) == "jsonb"
280-
arch_column_value = Json({"en_US": arch_db}) if jsonb_column else arch_db
281-
cr.execute(
282-
"""
283-
INSERT INTO ir_ui_view(name, "type", model, inherit_id, mode, active, priority, %s)
284-
VALUES(%%(name)s, %%(view_type)s, %%(model)s, %%(inherit_id)s, %%(mode)s, 't', %%(priority)s, %%(arch_db)s)
285-
RETURNING id
286-
"""
287-
% arch_col,
288-
{
289-
"name": name,
290-
"view_type": view_type,
291-
"model": model,
292-
"inherit_id": inherit_id,
293-
"mode": "extension" if inherit_id else "primary",
294-
"priority": priority,
295-
"arch_db": arch_column_value,
296-
},
297-
)
298-
return cr.fetchone()[0]
299-
300-
30156
# fmt:off
30257
if version_gte("saas~14.3"):
30358
def remove_asset(cr, name):

src/util/views/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)