From 9dd5bd93498bc57e5e1febf19119bb98cdd9b3d7 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Thu, 1 Dec 2016 17:07:12 +0100 Subject: [PATCH] [ADD][TEST] qweb: render a child view with its xmlid Issue: rendering a child view with its xmlid return its `read_template` wrapped in a "templates" tag. In the context of asset bundles, it leads to the browser not being able to handle it correctly. Explanation: What happens when you render a view? First, QWeb will call is method `get_template` with the argument `template`. This argument contains the name of template you try to render. It can be an id or an xmlid (also an etree but let's forget this one at the moment). Within the method `get_template`, it'll call IrQweb `load` method, again with the argument `template`. This `load` method will call IrUiView's `read_template` wih `template` as argument. This method will return the view in the database with all inheritance applied, wrapped in a "templates" tag. When `read_template` has returned, the `load` method will, according to `template` and in the following order: * if `template` is an xmlid, return the whole `read_template`. * if `template` is an id and if one of the first child has "t-name" attribute (whatever its value): return the subtree of this first child. * if `template` is an id and one of the first child is a "t" tag: set a "t-name" attribute to this first child with the id as value and return the subtree of this child. As you may note, inconsistencies already appear. Now that QWeb's `get_template` has finished its call to IRQweb's `load`, it'll do: * if one of the first child of what's returned as a "t-name" attribute matching the `template` argument, return this subtree * return the whole thing returned by IRQweb's `load`. Let's say we have a view:
something
What happens if we render this view with its xmlid? First Qweb's `get_template` will load the template with the help of IRQweb's `load` method. As it received an xmlid as argument, it'll return the whole `read_template` output, wrapped in a "templates" tag. Qweb's `get_template` will now check if one of the first child has a "t-name" attribude matching the `template` argument (base.dummy in our case). It has one, so it returns the subtree and the compilation continue. What happens if we render this view with its id? IRQweb's receives an id as argument, gets the `read_template` of it and check if one of the first child has a "t-name" attribute. It does, so it returns this subtree to `get_template`. The latest will check if one the first children has a matching "t-name" attribute with the `template` argument. It does not, so it returns the whole thing. Weird things in the way we got here but the result is the same whatever you tried to render the template with an id or an xmlid. Now let's add an extension to this view and try to render it (the extension): another thing What happens if we render this view with its xmlid? `load` will receive an id, so it'll return the whole `read_template` stuff. Note here that if you try to render a parent or child view, `read_template` will return the same thing: the parent with all inheritance applied. Now `get_template` looks through the first children of this to find a matching "t-name" and it doesn't find one, as the "t-name" is the one of the parent view and we tried to render with the child xmlid. It so returns the whole thing, wrapped in a "templates" tag. There's our issue. It's working if we tried to render the thing with its id because `load` would have returned the right subtree. This commit adds a test showing the failure. --- odoo/addons/base/tests/test_views.py | 41 ++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/odoo/addons/base/tests/test_views.py b/odoo/addons/base/tests/test_views.py index adbabdc48ad..e210ce90073 100644 --- a/odoo/addons/base/tests/test_views.py +++ b/odoo/addons/base/tests/test_views.py @@ -1183,3 +1183,44 @@ class TestXPathExtentions(common.BaseCase): self.assertEqual( len(tree.xpath('//node[hasclass("foo", "baz")]')), 1) + + +class TestQWebRender(ViewCase): + + def test_render(self): + view1 = self.View.create({ + 'name': "dummy", + 'type': 'qweb', + 'arch': """ + +
something
+
+ """ + }) + view2 = self.View.create({ + 'name': "dummy_ext", + 'type': 'qweb', + 'inherit_id': view1.id, + 'arch': """ + + another thing + + """ + }) + + # render with an id + content1 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id]).render(view1.id) + content2 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id]).render(view2.id) + + self.assertEqual(content1, content2) + + # render with an xmlid + self.env.cr.execute("INSERT INTO ir_model_data(name, model, res_id, module)" + "VALUES ('dummy', 'ir.ui.view', %s, 'base')" % view1.id) + self.env.cr.execute("INSERT INTO ir_model_data(name, model, res_id, module)" + "VALUES ('dummy_ext', 'ir.ui.view', %s, 'base')" % view2.id) + + content1 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id]).render('base.dummy') + content2 = self.env['ir.qweb'].with_context(check_view_ids=[view1.id, view2.id]).render('base.dummy_ext') + + self.assertEqual(content1, content2)