[ADD] qweb: handling of t-att=mapping

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 (?)
This commit is contained in:
Xavier Morel 2014-09-11 08:38:08 +02:00
parent 14a677090b
commit 2ffcff8fa9
3 changed files with 26 additions and 15 deletions

View 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(

View 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):

View 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)