diff --git a/odoo/addons/base/ir/ir_ui_view.py b/odoo/addons/base/ir/ir_ui_view.py
index 1a14fbfbfef..343aa6f570a 100644
--- a/odoo/addons/base/ir/ir_ui_view.py
+++ b/odoo/addons/base/ir/ir_ui_view.py
@@ -153,6 +153,7 @@ xpath_utils = etree.FunctionNamespace(None)
xpath_utils['hasclass'] = _hasclass
TRANSLATED_ATTRS_RE = re.compile(r"@(%s)\b" % "|".join(TRANSLATED_ATTRS))
+WRONGCLASS = re.compile(r"(@class\s*=|=\s*@class|contains\(@class)")
class View(models.Model):
@@ -301,6 +302,12 @@ actual arch.
if match:
message = "View inheritance may not use attribute %r as a selector." % match.group(1)
self.raise_view_error(message, self.id)
+ if WRONGCLASS.search(node.get('expr', '')):
+ _logger.warn(
+ "Error-prone use of @class in view %s (%s): use the "
+ "hasclass(*classes) function to filter elements by "
+ "their classes", self.name, self.xml_id
+ )
else:
for attr in TRANSLATED_ATTRS:
if node.get(attr):
diff --git a/odoo/addons/base/res/res_partner.py b/odoo/addons/base/res/res_partner.py
index 7784cc231e6..c3f52ed6302 100644
--- a/odoo/addons/base/res/res_partner.py
+++ b/odoo/addons/base/res/res_partner.py
@@ -48,7 +48,7 @@ class FormatAddressMixin(models.AbstractModel):
if address_view_id and not self._context.get('no_address_format'):
#render the partner address accordingly to address_view_id
doc = etree.fromstring(arch)
- for address_node in doc.xpath("//div[@class='o_address_format']"):
+ for address_node in doc.xpath("//div[hasclass('o_address_format')]"):
Partner = self.env['res.partner'].with_context(no_address_format=True)
sub_view = Partner.fields_view_get(
view_id=address_view_id.id, view_type='form', toolbar=False, submenu=False)