From edf1f23a6e1ec6508dac9ca3c502f99ee1c0097a Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 22 Apr 2013 14:50:00 +0200 Subject: [PATCH] [ADD] depth-first iterator on views, refactor apply_view_inheritance to use it bzr revid: xmo@openerp.com-20130422125000-pg60ovrxejegenka --- openerp/addons/base/ir/ir_ui_view.py | 25 +++++++++++++++++++++++++ openerp/osv/orm.py | 21 ++++++++++----------- openerp/tests/test_views.py | 13 +++++++++++++ 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py index 69d77e58b44..13afd096b38 100644 --- a/openerp/addons/base/ir/ir_ui_view.py +++ b/openerp/addons/base/ir/ir_ui_view.py @@ -230,6 +230,31 @@ class view(osv.osv): for view in self.browse(cr, 1, view_ids, context) if not (view.groups_id and user_groups.isdisjoint(view.groups_id))] + def iter(self, cr, uid, view_id, model, exclude_base=False, context=None): + """ iterates on all of `view_id`'s descendants tree depth-first. + + If `exclude_base` is `False`, also yields `view_id` itself. It is + `False` by default to match the behavior of etree's Element.iter. + + :param int view_id: database id of the root view + :param str model: name of the view's related model (for filtering) + :param boolean exclude_base: whether `view_id` should be excluded from the iteration + :param context: + :return: iterator of (database_id, arch_field) pairs for all + descendants of `view_id` (including `view_id` itself if + `exclude_base` is `False`, the default) + """ + if not exclude_base: + [base] = self.browse(cr, uid, [view_id], context=context) + yield base.id, base.arch + + for arch, id in self.get_inheriting_views_arch( + cr, uid, view_id, model, context=context): + yield id, arch + for info in self.iter( + cr, uid, id, model, exclude_base=True, context=None): + yield info + def write(self, cr, uid, ids, vals, context=None): if not isinstance(ids, (list, tuple)): ids = [ids] diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 84ba1d1d2d0..69cb422e6c4 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -2068,16 +2068,16 @@ class BaseModel(object): raise AttributeError("View definition error for inherited view '%s' on model '%s': %s" % (child_view.xml_id, self._name, error_msg)) - def apply_inheritance_specs(source, specs_arch, inherit_id=None): - """ Apply an inheriting view. + def apply_inheritance_specs(source, descendant_id, specs_arch): + """ Apply an inheriting view (a descendant of the base view) Apply to a source architecture all the spec nodes (i.e. nodes describing where and what changes to apply to some parent architecture) given by an inheriting view. :param source: a parent architecture to modify + :param descendant_id: the database id of the descendant :param specs_arch: a modifying architecture in an inheriting view - :param inherit_id: the database id of the inheriting view :return: a modified source where the specs are applied """ @@ -2124,7 +2124,7 @@ class BaseModel(object): elif pos == 'before': node.addprevious(child) else: - raise_view_error("Invalid position value: '%s'" % pos, inherit_id) + raise_view_error("Invalid position value: '%s'" % pos, descendant_id) else: attrs = ''.join([ ' %s="%s"' % (attr, spec.get(attr)) @@ -2134,8 +2134,8 @@ class BaseModel(object): tag = "<%s%s>" % (spec.tag, attrs) if spec.get('version') and spec.get('version') != source.get('version'): raise_view_error("Mismatching view API version for element '%s': %r vs %r in parent view '%%(parent_xml_id)s'" % \ - (tag, spec.get('version'), source.get('version')), inherit_id) - raise_view_error("Element '%s' not found in parent view '%%(parent_xml_id)s'" % tag, inherit_id) + (tag, spec.get('version'), source.get('version')), descendant_id) + raise_view_error("Element '%s' not found in parent view '%%(parent_xml_id)s'" % tag, descendant_id) return source @@ -2149,11 +2149,10 @@ class BaseModel(object): are applied """ - sql_inherit = self.pool.get('ir.ui.view').get_inheriting_views_arch(cr, user, inherit_id, self._name) - for (view_arch, view_id) in sql_inherit: - source = apply_inheritance_specs(source, view_arch, view_id) - source = apply_view_inheritance(cr, user, source, view_id) - return source + return reduce( + lambda s, descendant: apply_inheritance_specs(s, *descendant), + self.pool['ir.ui.view'].iter(cr, user, inherit_id, self._name), + source) result = {'type': view_type, 'model': self._name} diff --git a/openerp/tests/test_views.py b/openerp/tests/test_views.py index 9c964551c0c..8c3653eceda 100644 --- a/openerp/tests/test_views.py +++ b/openerp/tests/test_views.py @@ -139,3 +139,16 @@ class TestViewInheritance(common.TransactionCase): self.assertEqual(self.View.get_inheriting_views_arch( self.cr, self.uid, self.ids['A11'], self.model), [(self.view_for('A111'), self.ids['A111'])]) + + def test_iterate_descendents(self): + descendents = list(self.View.iter(self.cr, self.uid, self.ids['A1'], self.model)) + self.assertEqual(descendents, [ + (self.ids[name], self.view_for(name)) + for name in ['A1', 'A11', 'A111', 'A12'] + ]) + descendents = list(self.View.iter( + self.cr, self.uid, self.ids['A2'], self.model, exclude_base=True)) + self.assertEqual(descendents, [ + (self.ids[name], self.view_for(name)) + for name in ['A21', 'A22', 'A221'] + ])