diff --git a/openerp/addons/base/ir/ir_qweb.py b/openerp/addons/base/ir/ir_qweb.py index 29d69f09692..2811521ec32 100644 --- a/openerp/addons/base/ir/ir_qweb.py +++ b/openerp/addons/base/ir/ir_qweb.py @@ -27,6 +27,7 @@ import openerp.http import openerp.tools from openerp.tools.func import lazy_property import openerp.tools.lru +from openerp.exceptions import QWebException from openerp.fields import Datetime from openerp.http import request from openerp.tools.safe_eval import safe_eval as eval @@ -43,15 +44,6 @@ MAX_CSS_RULES = 4095 #-------------------------------------------------------------------- # QWeb template engine #-------------------------------------------------------------------- -class QWebException(Exception): - def __init__(self, message, **kw): - Exception.__init__(self, message) - self.qweb = dict(kw) - def pretty_xml(self): - if 'node' not in self.qweb: - return '' - return etree.tostring(self.qweb['node'], pretty_print=True) - class QWebTemplateNotFound(QWebException): pass diff --git a/openerp/exceptions.py b/openerp/exceptions.py index 209f2a4cff2..8d0bb779d3e 100644 --- a/openerp/exceptions.py +++ b/openerp/exceptions.py @@ -10,8 +10,9 @@ treated as a 'Server error'. If you consider introducing new exceptions, check out the test_exceptions addon. """ -from inspect import currentframe import logging +from inspect import currentframe +from lxml import etree from tools.func import frame_codeinfo _logger = logging.getLogger(__name__) @@ -90,3 +91,13 @@ class DeferredException(Exception): def __init__(self, msg, tb): self.message = msg self.traceback = tb + +class QWebException(Exception): + def __init__(self, message, **kw): + super(QWebException, self).__init__(message) + self.qweb = dict(kw) + + def pretty_xml(self): + if 'node' not in self.qweb: + return '' + return etree.tostring(self.qweb['node'], pretty_print=True) diff --git a/openerp/http.py b/openerp/http.py index cd608492f1a..1d5226d3430 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -306,7 +306,11 @@ class WebRequest(object): # case, the request cursor is unusable. Rollback transaction to create a new one. if self._cr: self._cr.rollback() - return self.endpoint(*a, **kw) + result = self.endpoint(*a, **kw) + if isinstance(result, Response) and result.is_qweb: + # Early rendering of lazy responses to benefit from @service_model.check protection + result.flatten() + return result if self.db: return checked_call(self.db, *args, **kwargs) @@ -1299,8 +1303,9 @@ class Response(werkzeug.wrappers.Response): """ Forces the rendering of the response's template, sets the result as response body and unsets :attr:`.template` """ - self.response.append(self.render()) - self.template = None + if self.template: + self.response.append(self.render()) + self.template = None class DisableCacheMiddleware(object): def __init__(self, app): diff --git a/openerp/service/model.py b/openerp/service/model.py index d1a2d103cd6..decfd5d8ba1 100644 --- a/openerp/service/model.py +++ b/openerp/service/model.py @@ -8,7 +8,7 @@ import threading import time import openerp -from openerp.exceptions import UserError, ValidationError +from openerp.exceptions import UserError, ValidationError, QWebException from openerp.tools.translate import translate from openerp.tools.translate import _ @@ -111,7 +111,13 @@ def check(f): if openerp.registry(dbname)._init and not openerp.tools.config['test_enable']: raise openerp.exceptions.Warning('Currently, this database is not fully loaded and can not be used.') return f(dbname, *args, **kwargs) - except OperationalError, e: + except (OperationalError, QWebException) as e: + if isinstance(e, QWebException): + cause = e.qweb.get('cause') + if isinstance(cause, OperationalError): + e = cause + else: + raise # Automatically retry the typical transaction serialization errors if e.pgcode not in PG_CONCURRENCY_ERRORS_TO_RETRY: raise