From e04983ae69b3efae3f4faccebf31597752d80b52 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 18 Dec 2013 15:09:17 +0100 Subject: [PATCH] [IMP] conversion back from m2o t-field to openerp value Instead of doing a name_get on the edited value and trying to find out an m2o to assign back (which there's pretty much no chance of given there's no autocompletion or anything), alter the m2o record in-place by setting the provided edited value to its _rec_name. Ideally, both features could be supported via more advanced m2o edition widgets which would allow selecting an existing m2o, creating a new m2o record from scratch or (maybe) editing the existing m2o's display_name if possible, somewhat similar to what the form view provides. Without these though, the only action which makes any sense is to edit the user-visible "value" where it is found, as with more normal fields. bzr revid: xmo@openerp.com-20131218140917-4eo2o55yfcumzhov --- addons/website/models/ir_qweb.py | 35 ++++++++++++---- addons/website/models/ir_ui_view.py | 8 ++-- addons/website/tests/test_converter.py | 58 +++++++++++++++++++------- 3 files changed, 75 insertions(+), 26 deletions(-) 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" + )