summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Torres <adt@odoo.com>2018-03-09 15:44:39 +0100
committerGitHub <noreply@github.com>2018-03-09 15:44:39 +0100
commitbbfb098a5459fdfe7c9fac9e86407c22457fd505 (patch)
tree025635a05d66b66d3d27a7fb7db2dbbe402331a5
parented4cbf57dc8d5850bf1e700dea0c35ea2f64c191 (diff)
[IMP] fields: allow shared fields
Given the following variables: * Module A * Module B * Model M * Field X of model M If module A defines M.X and is installed, an xmlid for this field is generated in the form of A.field_M_X Before this commit: If module B defines M.X as well and is installed after module A, no xmlid is generated. This means that if module A is uninstalled, the single xmlid pointing to M.X will be deleted and thus, the field itself will be deleted as well, therefore any views from module B referencing M.X will crash. After this commit: If module B (or any subsequent modules) define M.X, an xmlid will be generated in the form of B.field_M_X. If module A is uninstalled, the xmlid A.field_M_X will be deleted, but B.field_M_X will remain and thus the field itself won't be deleted. This system means that for a single actual field, there can be multiple xmlids, each xmlid sharing the same field, thus the name "shared fields". Task ID 38016
-rw-r--r--odoo/addons/base/models/ir_model.py44
-rw-r--r--odoo/fields.py7
2 files changed, 31 insertions, 20 deletions
diff --git a/odoo/addons/base/models/ir_model.py b/odoo/addons/base/models/ir_model.py
index 50588e9f3e9..96187c76a32 100644
--- a/odoo/addons/base/models/ir_model.py
+++ b/odoo/addons/base/models/ir_model.py
@@ -746,39 +746,45 @@ class IrModelFields(models.Model):
fields_data = self._existing_field_data(field.model_name)
field_data = fields_data.get(field.name)
params = self._reflect_field_params(field)
+ cr = self.env.cr
+ created = False
if field_data is None:
- cr = self.env.cr
- # create an entry in this table
+ # does not exist, create an entry in this table
query_insert(cr, self._table, params)
record = self.browse(cr.fetchone())
self.pool.post_init(record.modified, list(params))
- # create a corresponding xml id
- module = field._module or self._context.get('module')
- if module:
- model_name = field.model_name.replace('.', '_')
- xmlid = 'field_%s__%s' % (model_name, field.name)
- cr.execute(""" INSERT INTO ir_model_data (module, name, model, res_id, date_init, date_update)
- VALUES (%s, %s, %s, %s, (now() at time zone 'UTC'), (now() at time zone 'UTC')) """,
- (module, xmlid, record._name, record.id))
# update fields_data (for recursive calls)
fields_data[field.name] = dict(params, id=record.id)
- return record
+ created = True
- diff = {key for key, val in params.items() if field_data[key] != val}
- if diff:
- cr = self.env.cr
- # update the entry in this table
+ elif any(field_data[key] != val for key, val in params.items()):
+ # exists, update the entry in this table
query_update(cr, self._table, params, ['model', 'name'])
record = self.browse(cr.fetchone())
- self.pool.post_init(record.modified, diff)
+ names = [key for key, val in params.items() if field_data[key] != val]
+ self.pool.post_init(record.modified, names)
# update fields_data (for recursive calls)
field_data.update(params)
- return record
else:
- # nothing to update, simply return the corresponding record
- return self.browse(field_data['id'])
+ # exists, but nothing to update
+ record = self.browse(field_data['id'])
+
+ # generate xmlids if necessary, one per module defining the same field
+ module = self._context.get('module')
+ if module and (created or module in field._modules):
+ model_name = field.model_name.replace('.', '_')
+ xmlid = 'field_%s__%s' % (model_name, field.name)
+ cr.execute(
+ """
+ INSERT INTO ir_model_data (module, name, model, res_id, date_init, date_update)
+ SELECT %s, %s, %s, %s, (now() at time zone 'UTC'), (now() at time zone 'UTC')
+ WHERE NOT EXISTS (SELECT id FROM ir_model_data WHERE module=%s AND name=%s)
+ """, (module, xmlid, record._name, record.id, module, xmlid)
+ )
+
+ return record
def _reflect_model(self, model):
""" Reflect the given model's fields. """
diff --git a/odoo/fields.py b/odoo/fields.py
index e0ea484eb89..4050c07224c 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -272,8 +272,9 @@ class Field(MetaField('DummyField', (object,), {})):
'args': EMPTY_DICT, # the parameters given to __init__()
'_attrs': EMPTY_DICT, # the field's non-slot attributes
'_module': None, # the field's module name
+ '_modules': None, # modules that define this field
'_setup_done': None, # the field's setup state: None, 'base' or 'full'
- '_sequence': None, # absolute ordering of the field
+ '_sequence': None, # absolute ordering of the field
'automatic': False, # whether the field is automatically created ("magic" field)
'inherited': False, # whether the field is inherited (_inherits)
@@ -391,16 +392,20 @@ class Field(MetaField('DummyField', (object,), {})):
def _get_attrs(self, model, name):
""" Return the field parameter attributes as a dictionary. """
# determine all inherited field attributes
+ modules = set()
attrs = {}
if not (self.args.get('automatic') or self.args.get('manual')):
# magic and custom fields do not inherit from parent classes
for field in reversed(resolve_mro(model, name, self._can_setup_from)):
attrs.update(field.args)
+ if '_module' in field.args:
+ modules.add(field.args['_module'])
attrs.update(self.args) # necessary in case self is not in class
attrs['args'] = self.args
attrs['model_name'] = model._name
attrs['name'] = name
+ attrs['_modules'] = modules
# initialize ``self`` with ``attrs``
if attrs.get('compute'):