diff --git a/addons/mail/models/mail_render_mixin.py b/addons/mail/models/mail_render_mixin.py index 8ad6fe4a913..75929065faa 100644 --- a/addons/mail/models/mail_render_mixin.py +++ b/addons/mail/models/mail_render_mixin.py @@ -289,7 +289,7 @@ class MailRenderMixin(models.AbstractModel): :return dict: {res_id: string of rendered template based on record} """ results = dict.fromkeys(res_ids, u"") - if not template_src: + if not template_src or not res_ids: return results # prepare template variables @@ -341,11 +341,9 @@ class MailRenderMixin(models.AbstractModel): :return dict: {res_id: string of rendered template based on record} """ - # prevent wrong values (rendering on a void record set, ...) - if any(r is None for r in res_ids): - raise ValueError(_('Template rendering should be called on a valid record IDs.')) - results = {} + if not res_ids: + return results # prepare template variables variables = self._render_eval_context() @@ -384,12 +382,8 @@ class MailRenderMixin(models.AbstractModel): :return dict: {res_id: string of rendered template based on record} """ - # prevent wrong values (rendering on a void record set, ...) - if any(r is None for r in res_ids): - raise ValueError(_('Template rendering should be called on a valid record IDs.')) - results = dict.fromkeys(res_ids, u"") - if not template_txt: + if not template_txt or not res_ids: return results template_instructions = parse_inline_template(str(template_txt)) @@ -554,6 +548,12 @@ class MailRenderMixin(models.AbstractModel): :return dict: {res_id: string of rendered template based on record} """ + if field not in self: + raise ValueError( + _('Rendering of %(field_name)s is not possible as not defined on template.', + field_name=field + ) + ) if options is None: options = {} diff --git a/addons/mail/tests/test_mail_render.py b/addons/mail/tests/test_mail_render.py index b196063043c..b97f9a0305f 100644 --- a/addons/mail/tests/test_mail_render.py +++ b/addons/mail/tests/test_mail_render.py @@ -97,6 +97,13 @@ class TestMailRenderCommon(common.MailCommon): Autre Narrateur
""" ] + cls.base_rendered_void = [ + 'Hello
', + 'Hello
', + """+ English Speaker +
""" + ] # link to mail template cls.test_template = cls.env['mail.template'].create({ @@ -198,6 +205,43 @@ class TestMailRender(TestMailRenderCommon): )[partner.id] self.assertEqual(rendered, expected) + @users('employee') + def test_render_field_no_records(self): + """ Test rendering on void IDs, or a list with dummy / falsy ID """ + template = self.test_template.with_env(self.env) + partner = self.render_object.with_env(self.env) + for res_ids in ([], (), [False], [''], [None], [False, partner.id]): # various corner cases + for fname, expected_obj, expected_void in zip(['subject', 'body_html'], self.base_rendered, self.base_rendered_void): + with self.subTest(): + rendered_all = template._render_field( + fname, + res_ids, + compute_lang=True + ) + if res_ids: + self.assertTrue(res_ids[0] in rendered_all, + f'Rendering: key {repr(res_ids[0])} is considered as valid and should have an entry') + self.assertEqual(rendered_all[res_ids[0]], expected_void) + if len(res_ids) == 2: # second is partner + self.assertTrue(res_ids[1] in rendered_all) + self.assertEqual(rendered_all[res_ids[1]], expected_obj) + if not res_ids: + self.assertFalse(rendered_all, + 'Rendering: void input -> void output') + + @users('employee') + def test_render_field_not_existing(self): + """ Test trying to render a not-existing field: raise a proper ValueError + instead of crashing / raising a KeyError """ + template = self.env['mail.template'].browse(self.test_template.ids) + partner = self.env['res.partner'].browse(self.render_object_fr.ids) + with self.assertRaises(ValueError): + _rendered = template._render_field( + 'not_existing', + partner.ids, + compute_lang=True + )[partner.id] + @users('employee') def test_render_template_inline_template(self): partner = self.env['res.partner'].browse(self.render_object.ids)