summaryrefslogtreecommitdiff
path: root/odoo/fields.py
diff options
context:
space:
mode:
authorSébastien Theys <seb@odoo.com>2019-08-12 15:16:10 +0000
committerSébastien Theys <seb@odoo.com>2019-08-20 14:09:01 +0000
commit064ebe3b1aa359655f7e4966e682c15cc711e9f9 (patch)
tree603b51b77aa2f8dcb5c39922b9fab28ed61d6fe0 /odoo/fields.py
parent72d9167c3af2a18700f607913b420e5a0ddb9a52 (diff)
[IMP] fields, test_new_api: add ondelete support for m2m
Each model of the relation can define `ondelete` for its `column2` foreign key. This way the behavior is similar to m2o where the target of the `ondelete` is the `comodel` foreign key. Some models were already defining `ondelete` 'restrict' on m2m fields and after review it appears the behavior introduced in this commit is the expected behavior for them. The 'restrict' from product.product has to be removed however because the rest of the code needs adaptation now that it is properly restricted. These changes will be done on a separate PR. closes odoo/odoo#35654 Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
Diffstat (limited to 'odoo/fields.py')
-rw-r--r--odoo/fields.py26
1 files changed, 23 insertions, 3 deletions
diff --git a/odoo/fields.py b/odoo/fields.py
index e8c1d4bbb84..3c24223d083 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -3003,6 +3003,7 @@ class Many2many(_RelationalMulti):
'column2': None, # column of table referring to comodel
'auto_join': False, # whether joins are generated upon search
'limit': None, # optional limit to use upon read
+ 'ondelete': None, # optional ondelete for the column2 fkey
}
def __init__(self, comodel_name=Default, relation=Default, column1=Default,
@@ -3018,6 +3019,18 @@ class Many2many(_RelationalMulti):
def _setup_regular_base(self, model):
super(Many2many, self)._setup_regular_base(model)
+ # 3 cases:
+ # 1) The ondelete attribute is not defined, we assign it a sensible default
+ # 2) The ondelete attribute is defined and its definition makes sense
+ # 3) The ondelete attribute is explicitly defined as 'set null' for a m2m,
+ # this is considered a programming error.
+ self.ondelete = self.ondelete or 'cascade'
+ if self.ondelete == 'set null':
+ raise ValueError(
+ "The m2m field %s of model %s declares its ondelete policy "
+ "as being 'set null'. Only 'restrict' and 'cascade' make sense."
+ % (self.name, model._name)
+ )
if self.store:
if not (self.relation and self.column1 and self.column2):
self._explicit = False
@@ -3073,8 +3086,8 @@ class Many2many(_RelationalMulti):
if not self.manual:
model.pool.post_init(model.env['ir.model.relation']._reflect_relation,
model, self.relation, self._module)
+ comodel = model.env[self.comodel_name]
if not sql.table_exists(cr, self.relation):
- comodel = model.env[self.comodel_name]
query = """
CREATE TABLE "{rel}" ("{id1}" INTEGER NOT NULL,
"{id2}" INTEGER NOT NULL,
@@ -3087,18 +3100,25 @@ class Many2many(_RelationalMulti):
_schema.debug("Create table %r: m2m relation between %r and %r", self.relation, model._table, comodel._table)
model.pool.post_init(self.update_db_foreign_keys, model)
return True
+ elif sql.table_kind(cr, comodel._table) != 'v' and self.ondelete != 'cascade':
+ # Fix foreign key references with ondelete, unless the targets are
+ # SQL views.
+ # This is needed because by default the ondelete of the column1
+ # fkey is set to 'cascade', but the relation on the opposite model
+ # can override it by defining ondelete for its column2 fkey.
+ sql.fix_foreign_key(cr, self.relation, self.column2, comodel._table, 'id', self.ondelete)
def update_db_foreign_keys(self, model):
""" Add the foreign keys corresponding to the field's relation table. """
cr = model._cr
comodel = model.env[self.comodel_name]
reflect = model.env['ir.model.constraint']._reflect_constraint
- # create foreign key references with ondelete=cascade, unless the targets are SQL views
+ # create foreign key references with ondelete, unless the targets are SQL views
if sql.table_kind(cr, model._table) != 'v':
sql.add_foreign_key(cr, self.relation, self.column1, model._table, 'id', 'cascade')
reflect(model, '%s_%s_fkey' % (self.relation, self.column1), 'f', None, self._module)
if sql.table_kind(cr, comodel._table) != 'v':
- sql.add_foreign_key(cr, self.relation, self.column2, comodel._table, 'id', 'cascade')
+ sql.add_foreign_key(cr, self.relation, self.column2, comodel._table, 'id', self.ondelete)
reflect(model, '%s_%s_fkey' % (self.relation, self.column2), 'f', None, self._module)
def read(self, records):