Skip to content

[IMP] util.remove_view : remove redundant t-calls #116

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions src/base/tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -1825,3 +1825,101 @@ def test_convert_field_to_html(self):

self.assertEqual(default.json_value, '"<p>Test text</p>"')
self.assertEqual(partner.x_testx, "<p>test partner field</p>")


class TestRemoveView(UnitTestCase):
def test_remove_view(self):
test_view_1 = self.env["ir.ui.view"].create(
{
"name": "test_view_1",
"type": "qweb",
"key": "base.test_view_1",
"arch": """
<t t-name="base.test_view_1">
<div>Test View 1 Content</div>
</t>
""",
}
)
self.env["ir.model.data"].create(
{"name": "test_view_1", "module": "base", "model": "ir.ui.view", "res_id": test_view_1.id}
)
test_view_2 = self.env["ir.ui.view"].create(
{
"name": "test_view_2",
"type": "qweb",
"key": "base.test_view_2",
"arch": """
<t t-name="base.test_view_2">
<t t-call="base.test_view_1"/>
<div>Test View 2 Content</div>
</t>
""",
}
)
test_view_3 = self.env["ir.ui.view"].create(
{
"name": "test_view_3",
"type": "qweb",
"key": "base.test_view_3",
"arch": """
<t t-name="base.test_view_3">
<t t-call="base.test_view_1"/>
<t t-call="base.test_view_2"/>
</t>
""",
}
)
self.env["ir.model.data"].create(
{"name": "test_view_3", "module": "base", "model": "ir.ui.view", "res_id": test_view_3.id}
)

# call by xml_id
util.remove_view(self.env.cr, xml_id="base.test_view_1")
util.invalidate(test_view_2)
util.invalidate(test_view_3)
self.assertFalse(test_view_1.exists())
self.assertNotIn('t-call="base.test_view_1"', test_view_2.arch_db)
self.assertNotIn('t-call="base.test_view_1"', test_view_3.arch_db)

# call by view_id
util.remove_view(self.env.cr, view_id=test_view_2.id)
util.invalidate(test_view_3)
self.assertFalse(test_view_2.exists())
self.assertNotIn('t-call="base.test_view_2"', test_view_3.arch_db)


class TestRenameXMLID(UnitTestCase):
def test_rename_xmlid(self):
test_view_1 = self.env["ir.ui.view"].create(
{
"name": "test_view_1",
"type": "qweb",
"key": "base.test_view_1",
"arch": """
<t t-name="base.test_view_1">
<div>Test View 1 Content</div>
</t>
""",
}
)
self.env["ir.model.data"].create(
{"name": "test_view_1", "module": "base", "model": "ir.ui.view", "res_id": test_view_1.id}
)
test_view_2 = self.env["ir.ui.view"].create(
{
"name": "test_view_2",
"type": "qweb",
"key": "base.test_view_2",
"arch": """
<t t-name="base.test_view_2">
<t t-call="base.test_view_1"/>
<div>Test View 2 Content</div>
</t>
""",
}
)
util.rename_xmlid(self.env.cr, "base.test_view_1", "base.rename_view")
util.invalidate(test_view_2)
self.assertIn('t-call="base.rename_view"', test_view_2.arch_db)
self.assertIn('t-name="base.rename_view"', test_view_1.arch_db)
86 changes: 84 additions & 2 deletions src/util/records.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
from psycopg2.extras import Json, execute_values

try:
from odoo import release
from odoo import modules, release
from odoo.tools.convert import xml_import
from odoo.tools.misc import file_open
from odoo.tools.translate import xml_translate
except ImportError:
from openerp import release
from openerp import modules, release
from openerp.tools.convert import xml_import
from openerp.tools.misc import file_open

Expand Down Expand Up @@ -106,6 +106,16 @@ def remove_view(cr, xml_id=None, view_id=None, silent=False, key=None):
for [v_id] in cr.fetchall():
remove_view(cr, view_id=v_id, silent=silent, key=xml_id)

if not key and column_exists(cr, "ir_ui_view", "key"):
cr.execute("SELECT key FROM ir_ui_view WHERE id = %s and key != %s", [view_id, xml_id])
[key] = cr.fetchone() or [None]

# Occurrences of xml_id and key in the t-call of views are to be found and removed.
if xml_id != "?":
_remove_redundant_tcalls(cr, xml_id)
if key and key != xml_id:
_remove_redundant_tcalls(cr, key)

if not view_id:
return

Expand Down Expand Up @@ -794,6 +804,34 @@ def rename_xmlid(cr, old, new, noupdate=None, on_collision="fail"):
# Don't change the view keys unconditionally to avoid changing unrelated views.
cr.execute("UPDATE ir_ui_view SET key = %s WHERE key = %s", [new, old])

# Adapting t-call and t-name references in views
search_pattern = r"""\yt-(call|name)=(["']){}\2""".format(re.escape(old))
replace_pattern = r"t-\1=\2{}\2".format(new)
if version_gte("16.0"):
arch_col = get_value_or_en_translation(cr, "ir_ui_view", "arch_db")
replace_in_all_jsonb_values(
cr,
"ir_ui_view",
"arch_db",
PGRegexp(search_pattern),
replace_pattern,
extra_filter=cr.mogrify("{} ~ %s".format(arch_col), [search_pattern]).decode(),
)
else:
arch_col = "arch_db" if column_exists(cr, "ir_ui_view", "arch_db") else "arch"
cr.execute(
format_query(
cr,
"""
UPDATE ir_ui_view
SET {arch} = regexp_replace({arch}, %s, %s, 'g')
WHERE {arch} ~ %s
""",
arch=arch_col,
),
[search_pattern, replace_pattern, search_pattern],
)

if model == "ir.ui.menu" and column_exists(cr, "res_users_settings", "homemenu_config"):
query = """
UPDATE res_users_settings
Expand Down Expand Up @@ -1784,3 +1822,47 @@ def remove_act_window_view_mode(cr, model, view_mode):
""",
[view_mode, default, model, view_mode, view_mode],
)


def _remove_redundant_tcalls(cr, match):
"""
Remove t-calls of the removed view.

This function removes the t-calls to `match`.

:param str match: t-calls value to remove, typically it would be a view's xml_id or key
"""
arch_col = (
get_value_or_en_translation(cr, "ir_ui_view", "arch_db")
if column_exists(cr, "ir_ui_view", "arch_db")
else "arch"
)
cr.execute(
format_query(
cr,
"""
SELECT iv.id,
imd.module,
imd.name
FROM ir_ui_view iv
LEFT JOIN ir_model_data imd
ON iv.id = imd.res_id
AND imd.model = 'ir.ui.view'
WHERE {} ~ %s
""",
sql.SQL(arch_col),
),
[r"""\yt-call=(["']){}\1""".format(re.escape(match))],
)
standard_modules = set(modules.get_modules()) - {"studio_customization"}
for vid, module, name in cr.fetchall():
with edit_view(cr, view_id=vid) as arch:
for node in arch.findall(".//t[@t-call='{}']".format(match)):
node.getparent().remove(node)
if not module or module not in standard_modules:
_logger.info(
"The view %swith ID: %s has been updated, removed t-calls to deprecated %r",
("`{}.{}` ".format(module, name) if module else ""),
vid,
match,
)