[ADD] qweb: handling of t-att=mapping
authorXavier Morel <xmo@openerp.com>
Thu, 11 Sep 2014 06:38:08 +0000 (08:38 +0200)
committerXavier Morel <xmo@openerp.com>
Mon, 6 Oct 2014 17:13:44 +0000 (19:13 +0200)
Changed render_att_att to return an iterable of pairs instead of a pair, and
dispatched t-att on whether its result is a Mapping.

Also changed qweb test runner so it uses ordereddict for JSON mapping in
params, otherwise iteration order (and thus order of attributes in output) is
unpredictable and results don't/can't match expectations (as both are
strings).

Note that this relies on JS implementation details wrt iteration order of
mappings. Tests would probably be somewhat less brittle if rendering output
was parsed to XML... if that's possible (?)

addons/website/models/ir_qweb.py
openerp/addons/base/ir/ir_qweb.py
openerp/addons/base/tests/test_qweb.py

index 40b85fd..c598773 100644 (file)
@@ -56,11 +56,13 @@ class QWeb(orm.AbstractModel):
         super(QWeb, self).add_template(qcontext, name, node)
 
     def render_att_att(self, element, attribute_name, attribute_value, qwebcontext):
-        att, val = super(QWeb, self).render_att_att(element, attribute_name, attribute_value, qwebcontext)
+        URL_ATTRS = self.URL_ATTRS.get(element.tag)
+        is_website = request.website
+        for att, val in super(QWeb, self).render_att_att(element, attribute_name, attribute_value, qwebcontext):
+            if is_website and att == URL_ATTRS and isinstance(val, basestring):
+                val = qwebcontext.get('url_for')(val)
+            yield (att, val)
 
-        if request.website and att == self.URL_ATTRS.get(element.tag) and isinstance(val, basestring):
-            val = qwebcontext.get('url_for')(val)
-        return att, val
 
     def get_converter_for(self, field_type):
         return self.pool.get(
index d91d4cd..1508baf 100644 (file)
@@ -265,8 +265,12 @@ class QWeb(orm.AbstractModel):
             if attribute_name.startswith("t-"):
                 for attribute in self._render_att:
                     if attribute_name[2:].startswith(attribute):
-                        att, val = self._render_att[attribute](self, element, attribute_name, attribute_value, qwebcontext)
-                        if val:
+                        attrs = self._render_att[attribute](
+                            self, element, attribute_name, attribute_value, qwebcontext)
+                        for att, val in attrs:
+                            if not val: continue
+                            if not isinstance(val, str):
+                                val = unicode(val).encode('utf-8')
                             generated_attributes += self.render_attribute(element, att, val, qwebcontext)
                         break
                 else:
@@ -336,14 +340,16 @@ class QWeb(orm.AbstractModel):
     # Attributes
     def render_att_att(self, element, attribute_name, attribute_value, qwebcontext):
         if attribute_name.startswith("t-attf-"):
-            att, val = attribute_name[7:], self.eval_format(attribute_value, qwebcontext)
-        elif attribute_name.startswith("t-att-"):
-            att, val = attribute_name[6:], self.eval(attribute_value, qwebcontext)
-        else:
-            att, val = self.eval_object(attribute_value, qwebcontext)
-        if val and not isinstance(val, str):
-            val = unicode(val).encode("utf8")
-        return att, val
+            return [(attribute_name[7:], self.eval_format(attribute_value, qwebcontext))]
+
+        if attribute_name.startswith("t-att-"):
+            return [(attribute_name[6:], self.eval(attribute_value, qwebcontext))]
+
+        result = self.eval_object(attribute_value, qwebcontext)
+        if isinstance(result, collections.Mapping):
+            return result.iteritems()
+        # assume tuple
+        return [result]
 
     # Tags
     def render_tag_raw(self, element, template_attributes, generated_attributes, qwebcontext):
index a923a83..9ac4388 100644 (file)
@@ -4,6 +4,7 @@ import json
 import os.path
 import glob
 import re
+import collections
 
 from lxml import etree
 import openerp.addons.base.ir.ir_qweb
@@ -118,7 +119,9 @@ class TestQWeb(common.TransactionCase):
         for template in context.templates:
             if template.startswith('_'): continue
             param = doc.find('params[@id="{}"]'.format(template))
-            params = {} if param is None else json.loads(param.text)
+            # OrderedDict to ensure JSON mappings are iterated in source order
+            # so output is predictable & repeatable
+            params = {} if param is None else json.loads(param.text, object_pairs_hook=collections.OrderedDict)
 
             ctx = context.copy()
             ctx.update(params)