diff options
| author | Christophe Simonis <chs@odoo.com> | 2016-10-19 18:16:14 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2016-10-19 18:16:14 +0200 |
| commit | 938ffb92da1ea898f661aec04745f7630e2f378c (patch) | |
| tree | 7faeed8433101836cd0239cf6c23629ef1f1289d /odoo | |
| parent | 1f9e92dfef325b6727e1bee3de55bff095ec6cf2 (diff) | |
| parent | 6466aa499fc37bba5f4feeeecac99448ef88e62e (diff) | |
[MERGE] forward port branch 10.0 up to 6466aa4
Diffstat (limited to 'odoo')
| -rw-r--r-- | odoo/addons/base/ir/ir_model.py | 14 | ||||
| -rw-r--r-- | odoo/addons/base/ir/ir_ui_view.py | 17 | ||||
| -rw-r--r-- | odoo/addons/base/tests/test_views.py | 8 | ||||
| -rw-r--r-- | odoo/addons/test_impex/tests/test_export.py | 4 | ||||
| -rw-r--r-- | odoo/addons/test_new_api/tests/__init__.py | 1 | ||||
| -rw-r--r-- | odoo/addons/test_new_api/tests/test_onchange.py | 2 | ||||
| -rw-r--r-- | odoo/addons/test_new_api/tests/test_one2many.py | 65 | ||||
| -rw-r--r-- | odoo/addons/test_pylint/tests/test_pylint.py | 1 | ||||
| -rw-r--r-- | odoo/fields.py | 16 | ||||
| -rw-r--r-- | odoo/models.py | 12 | ||||
| -rw-r--r-- | odoo/tools/config.py | 2 |
11 files changed, 119 insertions, 23 deletions
diff --git a/odoo/addons/base/ir/ir_model.py b/odoo/addons/base/ir/ir_model.py index b4376c99467..1e23c311da2 100644 --- a/odoo/addons/base/ir/ir_model.py +++ b/odoo/addons/base/ir/ir_model.py @@ -1208,6 +1208,7 @@ class IrModelData(models.Model): datas = self.search([('module', 'in', modules_to_remove)]) wkf_todo = [] to_unlink = tools.OrderedSet() + undeletable = self.browse([]) for data in datas.sorted(key='id', reverse=True): model = data.model @@ -1232,6 +1233,7 @@ class IrModelData(models.Model): _logger.info('Unable to force processing of workflow for item %s@%s in order to leave activity to be deleted', res_id, model, exc_info=True) def unlink_if_refcount(to_unlink): + undeletable = self.browse() for model, res_id in to_unlink: external_ids = self.search([('model', '=', model), ('res_id', '=', res_id)]) if external_ids - datas: @@ -1255,28 +1257,30 @@ class IrModelData(models.Model): self.env[model].browse(res_id).unlink() except Exception: _logger.info('Unable to delete %s@%s', res_id, model, exc_info=True) + undeletable += external_ids self._cr.execute('ROLLBACK TO SAVEPOINT record_unlink_save') else: self._cr.execute('RELEASE SAVEPOINT record_unlink_save') + return undeletable # Remove non-model records first, then model fields, and finish with models - unlink_if_refcount(item for item in to_unlink if item[0] not in ('ir.model', 'ir.model.fields', 'ir.model.constraint')) - unlink_if_refcount(item for item in to_unlink if item[0] == 'ir.model.constraint') + undeletable += unlink_if_refcount(item for item in to_unlink if item[0] not in ('ir.model', 'ir.model.fields', 'ir.model.constraint')) + undeletable += unlink_if_refcount(item for item in to_unlink if item[0] == 'ir.model.constraint') modules = self.env['ir.module.module'].search([('name', 'in', modules_to_remove)]) constraints = self.env['ir.model.constraint'].search([('module', 'in', modules.ids)]) constraints._module_data_uninstall() - unlink_if_refcount(item for item in to_unlink if item[0] == 'ir.model.fields') + undeletable += unlink_if_refcount(item for item in to_unlink if item[0] == 'ir.model.fields') relations = self.env['ir.model.relation'].search([('module', 'in', modules.ids)]) relations._module_data_uninstall() - unlink_if_refcount(item for item in to_unlink if item[0] == 'ir.model') + undeletable += unlink_if_refcount(item for item in to_unlink if item[0] == 'ir.model') self._cr.commit() - datas.unlink() + (datas - undeletable).unlink() @api.model def _process_end(self, modules): diff --git a/odoo/addons/base/ir/ir_ui_view.py b/odoo/addons/base/ir/ir_ui_view.py index 67c755581d3..10bf7c627b4 100644 --- a/odoo/addons/base/ir/ir_ui_view.py +++ b/odoo/addons/base/ir/ir_ui_view.py @@ -229,10 +229,11 @@ actual arch. for view, view_wo_lang in zip(self, self.with_context(lang=None)): view_wo_lang.arch = view.arch_base + @api.depends('write_date') def _compute_model_data_id(self): - # get the last ir_model_data record corresponding to self + # get the first ir_model_data record corresponding to self domain = [('model', '=', 'ir.ui.view'), ('res_id', 'in', self.ids)] - for data in self.env['ir.model.data'].search_read(domain, ['res_id']): + for data in self.env['ir.model.data'].search_read(domain, ['res_id'], order='id desc'): view = self.browse(data['res_id']) view.model_data_id = data['id'] @@ -343,7 +344,15 @@ actual arch. values['name'] = "%s %s" % (values.get('model'), values['type']) self.clear_caches() - return super(View, self).create(self._compute_defaults(values)) + + if 'install_mode_data' in self._context: + # the view is created from a data file by installing a module; delay + # the recomputation of field 'model_data_id' until its xmlid record + # is created + with self.env.norecompute(): + return super(View, self).create(self._compute_defaults(values)) + else: + return super(View, self).create(self._compute_defaults(values)) @api.multi def write(self, vals): @@ -1168,4 +1177,4 @@ actual arch. try: self.browse(vid)._check_xml() except Exception as e: - self.raise_view_error("Can't validate view: %s" % e.message, vid) + self.raise_view_error("Can't validate view:\n%s" % (e.message or repr(e)), vid) diff --git a/odoo/addons/base/tests/test_views.py b/odoo/addons/base/tests/test_views.py index 38f30308fab..adbabdc48ad 100644 --- a/odoo/addons/base/tests/test_views.py +++ b/odoo/addons/base/tests/test_views.py @@ -12,6 +12,14 @@ from odoo.tests import common from odoo.tools import mute_logger +class ViewXMLID(common.TransactionCase): + def test_model_data_id(self): + """ Check whether views know their xmlid record. """ + view = self.env.ref('base.view_company_form') + self.assertTrue(view) + self.assertTrue(view.model_data_id) + self.assertEqual(view.model_data_id.complete_name, 'base.view_company_form') + class ViewCase(common.TransactionCase): def setUp(self): super(ViewCase, self).setUp() diff --git a/odoo/addons/test_impex/tests/test_export.py b/odoo/addons/test_impex/tests/test_export.py index e80f6df0866..226ba9943f6 100644 --- a/odoo/addons/test_impex/tests/test_export.py +++ b/odoo/addons/test_impex/tests/test_export.py @@ -23,7 +23,7 @@ class CreatorCase(common.TransactionCase): def export(self, value, fields=('value',), context=None): record = self.make(value) record.invalidate_cache() - return record._BaseModel__export_rows([f.split('/') for f in fields]) + return record._export_rows([f.split('/') for f in fields]) class test_boolean_field(CreatorCase): @@ -449,7 +449,7 @@ class test_o2m_multiple(CreatorCase): def export(self, value=None, fields=('child1', 'child2',), context=None, **values): record = self.make(value, **values) - return record._BaseModel__export_rows([f.split('/') for f in fields]) + return record._export_rows([f.split('/') for f in fields]) def test_empty(self): self.assertEqual( diff --git a/odoo/addons/test_new_api/tests/__init__.py b/odoo/addons/test_new_api/tests/__init__.py index 2bbcfe40990..b90ccc96825 100644 --- a/odoo/addons/test_new_api/tests/__init__.py +++ b/odoo/addons/test_new_api/tests/__init__.py @@ -3,5 +3,6 @@ from . import test_new_fields from . import test_onchange from . import test_attributes +from . import test_one2many from . import test_ui from . import test_domain diff --git a/odoo/addons/test_new_api/tests/test_onchange.py b/odoo/addons/test_new_api/tests/test_onchange.py index 87fc8229b7c..a6e2bff1b35 100644 --- a/odoo/addons/test_new_api/tests/test_onchange.py +++ b/odoo/addons/test_new_api/tests/test_onchange.py @@ -176,7 +176,7 @@ class TestOnChange(common.TransactionCase): self.assertEqual(values, { 'name': partner.name, 'partner': partner.id, - 'lines': [(5,), (4, line.id)], + 'lines': [(6, 0, [line.id])], }) # modify 'partner' diff --git a/odoo/addons/test_new_api/tests/test_one2many.py b/odoo/addons/test_new_api/tests/test_one2many.py new file mode 100644 index 00000000000..38dee13ab54 --- /dev/null +++ b/odoo/addons/test_new_api/tests/test_one2many.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +from openerp.tests.common import TransactionCase + + +class One2manyCase(TransactionCase): + def setUp(self): + super(One2manyCase, self).setUp() + self.Line = self.env["test_new_api.multi.line"] + self.multi = self.env["test_new_api.multi"].create({ + "name": "What is up?" + }) + + def operations(self): + """Run operations on o2m fields to check all works fine.""" + # Check the lines first + self.assertItemsEqual( + self.multi.lines.mapped('name'), + map(str, range(10))) + # Modify the first line and drop the last one + self.multi.lines[0].name = "hello" + self.multi.lines = self.multi.lines[:-1] + self.assertEqual(len(self.multi.lines), 9) + self.assertIn("hello", self.multi.lines.mapped('name')) + # Invalidate the cache and check again; this crashes if the value + # of self.multi.lines in cache contains new records + self.multi.invalidate_cache() + self.assertEqual(len(self.multi.lines), 9) + self.assertIn("hello", self.multi.lines.mapped('name')) + + def test_new_one_by_one(self): + """Check lines created with ``new()`` and appended one by one.""" + for name in range(10): + self.multi.lines |= self.Line.new({"name": str(name)}) + self.operations() + + def test_new_single(self): + """Check lines created with ``new()`` and added in one step.""" + self.multi.lines = self.Line.browse( + [self.Line.new({"name": str(name)}).id for name in range(10)] + ) + self.operations() + + def test_create_one_by_one(self): + """Check lines created with ``create()`` and appended one by one.""" + for name in range(10): + self.multi.lines |= self.Line.create({"name": str(name)}) + self.operations() + + def test_create_single(self): + """Check lines created with ``create()`` and added in one step.""" + self.multi.lines = self.Line.browse( + [self.Line.create({"name": str(name)}).id for name in range(10)] + ) + self.operations() + + def test_rpcstyle_one_by_one(self): + """Check lines created with RPC style and appended one by one.""" + for name in range(10): + self.multi.lines = [(0, 0, {"name": str(name)})] + self.operations() + + def test_rpcstyle_single(self): + """Check lines created with RPC style and added in one step""" + self.multi.lines = [(0, 0, {'name': str(name)}) for name in range(10)] + self.operations() diff --git a/odoo/addons/test_pylint/tests/test_pylint.py b/odoo/addons/test_pylint/tests/test_pylint.py index 251db1f6eba..38fa4c3d51a 100644 --- a/odoo/addons/test_pylint/tests/test_pylint.py +++ b/odoo/addons/test_pylint/tests/test_pylint.py @@ -22,6 +22,7 @@ _logger = logging.getLogger(__name__) class TestPyLint(TransactionCase): ENABLED_CODES = [ + 'E0601', # using variable before assignment 'W0123', # eval used 'W0101', # unreachable code ] diff --git a/odoo/fields.py b/odoo/fields.py index ee732b5b62f..c542945a781 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -894,10 +894,12 @@ class Field(object): env.invalidate(spec) else: - # simply write to the database, and update cache + # Write to database write_value = self.convert_to_write(self.convert_to_record(value, record), record) record.write({self.name: write_value}) - record._cache[self] = value + # Update the cache unless value contains a new record + if not (self.relational and not all(value)): + record._cache[self] = value ############################################################################ # @@ -1987,7 +1989,7 @@ class _RelationalMulti(_Relational): def convert_to_write(self, value, record): # make result with new and existing records - result = [(5,)] + result = [(6, 0, [])] for record in value: if not record.id: values = {name: record[name] for name in record._cache} @@ -1998,7 +2000,7 @@ class _RelationalMulti(_Relational): values = record._convert_to_write(values) result.append((1, record.id, values)) else: - result.append((4, record.id)) + result[0][2].append(record.id) return result def convert_to_onchange(self, value, record, fnames=()): @@ -2171,7 +2173,11 @@ class One2many(_RelationalMulti): query = "SELECT id FROM %s WHERE %s=%%s AND id <> ALL(%%s)" % (comodel._table, inverse) comodel._cr.execute(query, (record.id, act[2] or [0])) lines = comodel.browse([row[0] for row in comodel._cr.fetchall()]) - lines.write({inverse: False}) + inverse_field = comodel._fields[inverse] + if inverse_field.ondelete == 'cascade': + lines.unlink() + else: + lines.write({inverse: False}) class Many2many(_RelationalMulti): diff --git a/odoo/models.py b/odoo/models.py index 9270782eadb..8f15a59d7cd 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -738,7 +738,7 @@ class BaseModel(object): return '__export__.' + name @api.multi - def __export_rows(self, fields): + def _export_rows(self, fields): """ Export fields of the records in ``self``. :param fields: list of lists of fields to traverse @@ -785,7 +785,7 @@ class BaseModel(object): # recursively export the fields that follow name fields2 = [(p[1:] if p and p[0] == name else []) for p in fields] - lines2 = value.__export_rows(fields2) + lines2 = value._export_rows(fields2) if lines2: # merge first line with record's main line for j, val in enumerate(lines2[0]): @@ -804,6 +804,9 @@ class BaseModel(object): return lines + # backward compatibility + __export_rows = _export_rows + @api.multi def export_data(self, fields_to_export, raw_data=False): """ Export fields for selected objects @@ -817,7 +820,7 @@ class BaseModel(object): fields_to_export = map(fix_import_export_id_paths, fields_to_export) if raw_data: self = self.with_context(export_raw_data=True) - return {'datas': self.__export_rows(fields_to_export)} + return {'datas': self._export_rows(fields_to_export)} @api.model def load(self, fields, data): @@ -3505,8 +3508,7 @@ class BaseModel(object): ``(6, _, ids)`` replaces all existing records in the set by the ``ids`` list, equivalent to using the command ``5`` followed by a command - ``4`` for each ``id`` in ``ids``. Can not be used on - :class:`~odoo.fields.One2many`. + ``4`` for each ``id`` in ``ids``. .. note:: Values marked as ``_`` in the list above are ignored and can be anything, generally ``0`` or ``False``. diff --git a/odoo/tools/config.py b/odoo/tools/config.py index 5584002c520..ea030a80f10 100644 --- a/odoo/tools/config.py +++ b/odoo/tools/config.py @@ -363,7 +363,7 @@ class configmanager(object): rcfilepath = old_rcfilepath self.rcfile = os.path.abspath( - self.config_file or opt.config or os.environ.get('OPENERP_SERVER') or rcfilepath) + self.config_file or opt.config or os.environ.get('ODOO_RC') or os.environ.get('OPENERP_SERVER') or rcfilepath) self.load() # Verify that we want to log or not, if not the output will go to stdout |
