diff --git a/addons/website/models/ir_qweb.py b/addons/website/models/ir_qweb.py index 64a374119fa..32e6a8d33e8 100644 --- a/addons/website/models/ir_qweb.py +++ b/addons/website/models/ir_qweb.py @@ -204,12 +204,29 @@ class ManyToOne(orm.AbstractModel): _inherit = ['website.qweb.field', 'ir.qweb.field.many2one'] def from_html(self, cr, uid, model, column, element, context=None): - # FIXME: this behavior is really weird, what if the user wanted to edit the name of the related thingy? Should m2os really be editable without a widget? - matches = self.pool[column._obj].name_search( - cr, uid, name=element.text_content().strip(), context=context) - # FIXME: no match? More than 1 match? - assert len(matches) == 1 - return matches[0][0] + # FIXME: layering violations all the things + Model = self.pool[element.get('data-oe-model')] + M2O = self.pool[column._obj] + field = element.get('data-oe-field') + id = int(element.get('data-oe-id')) + value = element.text_content().strip() + + # if anything blows up, just ignore it and bail + try: + # get parent record + [obj] = Model.read(cr, uid, [id], [field]) + # get m2o record id + (m2o_id, _) = obj[field] + # assume _rec_name and write directly to it + M2O.write(cr, uid, [m2o_id], { + M2O._rec_name: value + }, context=context) + except: + logger.exception("Could not save %r to m2o field %s of model %s", + value, field, Model._name) + + # not necessary, but might as well be explicit about it + return None class HTML(orm.AbstractModel): _name = 'website.qweb.field.html' @@ -281,7 +298,7 @@ class Image(orm.AbstractModel): match.group('module'), 'static', *(rest.split('/'))) if not path: - return False + return None try: with open(path, 'rb') as f: @@ -292,7 +309,7 @@ class Image(orm.AbstractModel): return f.read().encode('base64') except Exception: logger.exception("Failed to load local image %r", url) - return False + return None def load_remote_url(self, url): try: @@ -310,7 +327,7 @@ class Image(orm.AbstractModel): image.load() except Exception: logger.exception("Failed to load remote image %r", url) - return False + return None # don't use original data in case weird stuff was smuggled in, with # luck PIL will remove some of it? diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py index b935d6e3465..02fa87e10aa 100644 --- a/addons/website/models/ir_ui_view.py +++ b/addons/website/models/ir_ui_view.py @@ -70,9 +70,11 @@ class view(osv.osv): el.get('data-oe-type')) value = converter.from_html(cr, uid, Model, column, el) - Model.write(cr, uid, [int(el.get('data-oe-id'))], { - field: value - }, context=context) + if value is not None: + # TODO: batch writes? + Model.write(cr, uid, [int(el.get('data-oe-id'))], { + field: value + }, context=context) def to_field_ref(self, cr, uid, el, context=None): # filter out meta-information inserted in the document diff --git a/addons/website/tests/test_converter.py b/addons/website/tests/test_converter.py index 1086ec8e8d3..4b24e8f0b9f 100644 --- a/addons/website/tests/test_converter.py +++ b/addons/website/tests/test_converter.py @@ -63,20 +63,6 @@ class TestConvertBack(common.TransactionCase): self.field_roundtrip('char', "ⒸⓄⓇⒼⒺ") - def test_m2o(self): - Sub = self.registry('website.converter.test.sub') - sub = partial(Sub.create, self.cr, self.uid) - ids = [ - sub({'name': "Foo"}), - sub({'name': "Bar"}), - sub({'name': "Baz"}), - ] - - self.field_rountrip_result( - 'many2one', - ids[2], - ids[2]) - def test_selection(self): self.field_roundtrip('selection', 3) @@ -104,3 +90,47 @@ class TestConvertBack(common.TransactionCase): You never know You never know until you go """) + + def test_m2o(self): + """ the M2O field conversion (from html) is markedly different from + others as it directly writes into the m2o and returns nothing at all. + """ + model = 'website.converter.test' + field = 'many2one' + + Sub = self.registry('website.converter.test.sub') + sub_id = Sub.create(self.cr, self.uid, {'name': "Foo"}) + + Model = self.registry(model) + id = Model.create(self.cr, self.uid, {field: sub_id}) + [record] = Model.browse(self.cr, self.uid, [id]) + + e = document.createElement('span') + field_value = 'record.%s' % field + e.setAttribute('t-field', field_value) + + rendered = self.registry('website.qweb').render_tag_field( + e, {'field': field_value}, '', ir_qweb.QWebContext(self.cr, self.uid, { + 'record': record, + })) + + element = html.fromstring(rendered, parser=html.HTMLParser(encoding='utf-8')) + # emulate edition + element.text = "New content" + + column = Model._all_columns[field].column + converter = self.registry('website.qweb').get_converter_for( + element.get('data-oe-type')) + + value_back = converter.from_html( + self.cr, self.uid, model, column, element) + + self.assertIsNone( + value_back, "the m2o converter should return None to avoid spurious" + " or useless writes on the parent record") + + self.assertEqual( + Sub.browse(self.cr, self.uid, sub_id).name, + "New content", + "element edition should have been written directly to the m2o record" + )