[FIX] website: get only specific view children on _views_get()
Before this comit:
The `_views_get()` method would be called recursively for every
`inherit_children_ids`.
At the end of the method, we would get the complete view hierarchy, including
duplicate hierarchies for website specific copies (COW).
Then, the `get_related_views` that is calling `_views_get()` would be overriden
in website module to keep only most specific views.
But this would not be enough as it would leave orphans from other trees:
- Have a base view 'B'
- That has an inherited view 'I'
- That has itself an inherited view 'II'
- Now make the hierarchy specific on a website from 'I'
- And add another inherited view to 'I' on the generic tree
This become:
B
/ \
/ \
I I'
/ \ |
II II2 II'
As explained, filtering duplicates would not be enough as it would correctly
remove 'I' and 'II' from the generic tree but would not remove 'II2'.
At the end, it would return "B", "II2", "I'" and "II'".
Now, we correctly return only "B", "I'" and "II'" by ignoring generic trees
when there is a specific one.
Closes #29347
This commit is contained in:
@@ -199,6 +199,14 @@ class IrUiView(models.Model):
|
||||
self.sudo().model_data_id.write({'noupdate': True}) # TODO check if we remove this
|
||||
self.write({'arch': self._pretty_arch(new_arch)})
|
||||
|
||||
@api.model
|
||||
def _view_get_inherited_children(self, view, options):
|
||||
extensions = view.inherit_children_ids
|
||||
if not options:
|
||||
# only active children
|
||||
extensions = extensions.filtered(lambda view: view.active)
|
||||
return extensions
|
||||
|
||||
@api.model
|
||||
def _view_obj(self, view_id):
|
||||
if isinstance(view_id, pycompat.string_types):
|
||||
@@ -244,10 +252,7 @@ class IrUiView(models.Model):
|
||||
if called_view and called_view not in views_to_return:
|
||||
views_to_return += self._views_get(called_view, options=options, bundles=bundles)
|
||||
|
||||
extensions = view.inherit_children_ids
|
||||
if not options:
|
||||
# only active children
|
||||
extensions = view.inherit_children_ids.filtered(lambda view: view.active)
|
||||
extensions = self._view_get_inherited_children(view, options)
|
||||
|
||||
# Keep options in a deterministic order regardless of their applicability
|
||||
for extension in extensions.sorted(key=lambda v: v.id):
|
||||
|
||||
@@ -130,20 +130,7 @@ class View(models.Model):
|
||||
# method should never be called in a generic context, even for
|
||||
# tests)
|
||||
self = self.with_context(website_id=self.env['website'].get_current_website().id)
|
||||
views = super(View, self).get_related_views(key, bundles=bundles)
|
||||
current_website_id = self._context.get('website_id')
|
||||
most_specific_views = self.env['ir.ui.view']
|
||||
|
||||
if not current_website_id:
|
||||
return views
|
||||
|
||||
for view in views:
|
||||
if view.website_id and view.website_id.id == current_website_id:
|
||||
most_specific_views |= view
|
||||
elif not view.website_id and not any(view.key == view2.key and view2.website_id and view2.website_id.id == current_website_id for view2 in views):
|
||||
most_specific_views |= view
|
||||
|
||||
return most_specific_views
|
||||
return super(View, self).get_related_views(key, bundles=bundles)
|
||||
|
||||
@api.multi
|
||||
def _sort_suitability_key(self):
|
||||
@@ -167,6 +154,11 @@ class View(models.Model):
|
||||
filtered += sorted(group, key=lambda record: record._sort_suitability_key())[0]
|
||||
return (filtered + view_without_key).sorted(key=lambda view: (view.priority, view.id))
|
||||
|
||||
@api.model
|
||||
def _view_get_inherited_children(self, view, options):
|
||||
extensions = super(View, self)._view_get_inherited_children(view, options)
|
||||
return extensions.filter_duplicate()
|
||||
|
||||
@api.model
|
||||
def _view_obj(self, view_id):
|
||||
''' Given an xml_id or a view_id, return the corresponding view record.
|
||||
|
||||
@@ -590,3 +590,34 @@ class TestCowViewSaving(common.TransactionCase):
|
||||
self.assertEqual(view.active, True, "filter_duplicate should return the generic one")
|
||||
view = View.with_context(active_test=False, website_id=1).search([('key', '=', self.inherit_view.key)]).filter_duplicate()
|
||||
self.assertEqual(view.active, False, "filter_duplicate should return the specific one")
|
||||
|
||||
def test_get_related_views_tree(self):
|
||||
View = self.env['ir.ui.view']
|
||||
|
||||
self.base_view.write({'name': 'B', 'key': 'B'})
|
||||
self.inherit_view.write({'name': 'I', 'key': 'I'})
|
||||
View.create({
|
||||
'name': 'II',
|
||||
'mode': 'extension',
|
||||
'inherit_id': self.inherit_view.id,
|
||||
'arch': '<div position="inside">, sub ext</div>',
|
||||
'key': 'II',
|
||||
})
|
||||
self.inherit_view.with_context(website_id=1).write({'name': 'Extension'}) # Trigger cow on hierarchy
|
||||
View.create({
|
||||
'name': 'II2',
|
||||
'mode': 'extension',
|
||||
'inherit_id': self.inherit_view.id,
|
||||
'arch': '<div position="inside">, sub sibling specific</div>',
|
||||
'key': 'II2',
|
||||
})
|
||||
|
||||
# B
|
||||
# / \
|
||||
# / \
|
||||
# I I'
|
||||
# / \ |
|
||||
# II II2 II'
|
||||
|
||||
views = View.with_context(website_id=1).get_related_views('B')
|
||||
self.assertEqual(views.mapped('key'), ['B', 'I', 'II'], "Should only return the specific tree")
|
||||
|
||||
Reference in New Issue
Block a user