diff --git a/openerp/modules/module.py b/openerp/modules/module.py index 98955aff492..ec1c6a1708b 100644 --- a/openerp/modules/module.py +++ b/openerp/modules/module.py @@ -400,7 +400,7 @@ def load_openerp_module(module_name): initialize_sys_path() try: mod_path = get_module_path(module_name) - zip_mod_path = mod_path + '.zip' + zip_mod_path = '' if not mod_path else mod_path + '.zip' if not os.path.isfile(zip_mod_path): __import__('openerp.addons.' + module_name) else: diff --git a/openerp/modules/registry.py b/openerp/modules/registry.py index 4aa20f3349d..b06c500a24c 100644 --- a/openerp/modules/registry.py +++ b/openerp/modules/registry.py @@ -216,6 +216,11 @@ class RegistryManager(object): del cls.registries[db_name] raise + # load_modules() above can replace the registry by calling + # indirectly new() again (when modules have to be uninstalled). + # Yeah, crazy. + registry = cls.registries[db_name] + cr = registry.db.cursor() try: Registry.setup_multi_process_signaling(cr) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 67d365ecf6f..8a432240567 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1077,7 +1077,7 @@ class BaseModel(object): # Validate rec_name if self._rec_name is not None: - assert self._rec_name in self._columns.keys() + ['id'], "Invalid rec_name %s for model %s" % (self._rec_name, self._name) + assert self._rec_name in self._all_columns.keys() + ['id'], "Invalid rec_name %s for model %s" % (self._rec_name, self._name) else: self._rec_name = 'name' @@ -1362,11 +1362,9 @@ class BaseModel(object): noupdate=noupdate, res_id=id, context=context)) cr.execute('RELEASE SAVEPOINT model_load_save') except psycopg2.Warning, e: - _logger.exception('Failed to import record %s', record) messages.append(dict(info, type='warning', message=str(e))) cr.execute('ROLLBACK TO SAVEPOINT model_load_save') except psycopg2.Error, e: - _logger.exception('Failed to import record %s', record) messages.append(dict( info, type='error', **PGERROR_TO_OE[e.pgcode](self, fg, info, e))) @@ -5327,11 +5325,28 @@ def convert_pgerror_23502(model, fields, info, e): 'message': message, 'field': field_name, } +def convert_pgerror_23505(model, fields, info, e): + m = re.match(r'^duplicate key (?P\w+) violates unique constraint', + str(e)) + field_name = m.group('field') + if not m or field_name not in fields: + return {'message': unicode(e)} + message = _(u"The value for the field '%s' already exists.") % field_name + field = fields.get(field_name) + if field: + message = _(u"%s This might be '%s' in the current model, or a field " + u"of the same name in an o2m.") % (message, field['string']) + return { + 'message': message, + 'field': field_name, + } PGERROR_TO_OE = collections.defaultdict( # shape of mapped converters lambda: (lambda model, fvg, info, pgerror: {'message': unicode(pgerror)}), { # not_null_violation '23502': convert_pgerror_23502, + # unique constraint error + '23505': convert_pgerror_23505, }) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/report/report_sxw.py b/openerp/report/report_sxw.py index b1f1e3d5891..325690205ef 100644 --- a/openerp/report/report_sxw.py +++ b/openerp/report/report_sxw.py @@ -189,6 +189,8 @@ class rml_parse(object): return newtag, attrs def _ellipsis(self, char, size=100, truncation_str='...'): + if not char: + return '' if len(char) <= size: return char return char[:size-len(truncation_str)] + truncation_str diff --git a/openerp/service/workers.py b/openerp/service/workers.py index ed77b28cf1b..6d2da2d927b 100644 --- a/openerp/service/workers.py +++ b/openerp/service/workers.py @@ -319,7 +319,13 @@ class WorkerHTTP(Worker): fcntl.fcntl(client, fcntl.F_SETFD, flags) # do request using WorkerBaseWSGIServer monkey patched with socket self.server.socket = client - self.server.process_request(client,addr) + # tolerate broken pipe when the http client closes the socket before + # receiving the full reply + try: + self.server.process_request(client,addr) + except IOError, e: + if e.errno != errno.EPIPE: + raise self.request_count += 1 def process_work(self): diff --git a/openerp/tests/addons/test_impex/ir.model.access.csv b/openerp/tests/addons/test_impex/ir.model.access.csv index 9a674d9ee1f..fd0f7370c67 100644 --- a/openerp/tests/addons/test_impex/ir.model.access.csv +++ b/openerp/tests/addons/test_impex/ir.model.access.csv @@ -23,3 +23,4 @@ access_export_one2many_child_2,access_export_one2many_child_2,model_export_one2m access_export_many2many_other,access_export_many2many_other,model_export_many2many_other,,1,1,1,1 access_export_selection_withdefault,access_export_selection_withdefault,model_export_selection_withdefault,,1,1,1,1 access_export_one2many_recursive,access_export_one2many_recursive,model_export_one2many_recursive,,1,1,1,1 +access_export_unique,access_export_unique,model_export_unique,,1,1,1,1 diff --git a/openerp/tests/addons/test_impex/models.py b/openerp/tests/addons/test_impex/models.py index 95a7f90bda8..8c76850d628 100644 --- a/openerp/tests/addons/test_impex/models.py +++ b/openerp/tests/addons/test_impex/models.py @@ -144,3 +144,13 @@ class RecO2M(orm.Model): 'value': fields.integer(), 'child': fields.one2many('export.one2many.multiple', 'parent_id') } + +class OnlyOne(orm.Model): + _name = 'export.unique' + + _columns = { + 'value': fields.integer(), + } + _sql_constraints = [ + ('value_unique', 'unique (value)', "The value must be unique"), + ] diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index fe01a267d58..0afbf6f49c3 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -1149,3 +1149,29 @@ class test_datetime(ImporterCase): self.assertEqual( values(self.read(domain=[('id', 'in', result['ids'])])), ['2012-02-03 11:11:11']) + +class test_unique(ImporterCase): + model_name = 'export.unique' + + @mute_logger('openerp.sql_db') + def test_unique(self): + result = self.import_(['value'], [ + ['1'], + ['1'], + ['2'], + ['3'], + ['3'], + ]) + self.assertFalse(result['ids']) + self.assertEqual(result['messages'], [ + dict(message=u"The value for the field 'value' already exists. " + u"This might be 'unknown' in the current model, " + u"or a field of the same name in an o2m.", + type='error', rows={'from': 1, 'to': 1}, + record=1, field='value'), + dict(message=u"The value for the field 'value' already exists. " + u"This might be 'unknown' in the current model, " + u"or a field of the same name in an o2m.", + type='error', rows={'from': 4, 'to': 4}, + record=4, field='value'), + ])