[FIX] orm,registry: properly check m2o FKs during model update + fix some models `auto_init`ed multiple times
The case where no constraint existed at all was not working, and after fixing it, the ORM started to re-create the same constraints multiple times for some modules. This was for models that are split within a module (such as res.users in base, which is made of several small classes): all the partial models were going through _auto_init instead of only the final one - doing useless extra work. Unfortunately establishing the FK happens before the XML data files are loaded, so a number of FK and NOT NULL constraints failed to apply due to missing tables/records. base.sql had to be extended a bit to cover these cases in a minimalist fashion bzr revid: odo@openerp.com-20121217214645-av9n003yzterhujw
This commit is contained in:
parent
f827de7049
commit
9f77d2e2f4
|
@ -149,7 +149,6 @@ CREATE TABLE res_users (
|
||||||
active boolean default True,
|
active boolean default True,
|
||||||
login varchar(64) NOT NULL UNIQUE,
|
login varchar(64) NOT NULL UNIQUE,
|
||||||
password varchar(64) default null,
|
password varchar(64) default null,
|
||||||
lang varchar(64) default '',
|
|
||||||
-- No FK references below, will be added later by ORM
|
-- No FK references below, will be added later by ORM
|
||||||
-- (when the destination rows exist)
|
-- (when the destination rows exist)
|
||||||
company_id int,
|
company_id int,
|
||||||
|
@ -316,13 +315,29 @@ CREATE TABLE ir_module_module_dependency (
|
||||||
primary key(id)
|
primary key(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE res_company (
|
CREATE TABLE res_partner (
|
||||||
id serial NOT NULL,
|
id serial NOT NULL,
|
||||||
name character varying(64) not null,
|
name character varying(128),
|
||||||
parent_id integer references res_company on delete set null,
|
lang varchar(64),
|
||||||
|
company_id int,
|
||||||
primary key(id)
|
primary key(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE res_currency (
|
||||||
|
id serial PRIMARY KEY,
|
||||||
|
name VARCHAR(32) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE res_company (
|
||||||
|
id serial PRIMARY KEY,
|
||||||
|
name character varying(128) not null,
|
||||||
|
parent_id integer references res_company on delete set null,
|
||||||
|
partner_id integer not null references res_partner,
|
||||||
|
currency_id integer not null references res_currency
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE res_lang (
|
CREATE TABLE res_lang (
|
||||||
id serial PRIMARY KEY,
|
id serial PRIMARY KEY,
|
||||||
name VARCHAR(64) NOT NULL UNIQUE,
|
name VARCHAR(64) NOT NULL UNIQUE,
|
||||||
|
@ -375,16 +390,24 @@ CREATE TABLE ir_model_relation (
|
||||||
module integer NOT NULL references ir_module_module on delete restrict,
|
module integer NOT NULL references ir_module_module on delete restrict,
|
||||||
model integer NOT NULL references ir_model on delete restrict,
|
model integer NOT NULL references ir_model on delete restrict,
|
||||||
name character varying(128) NOT NULL
|
name character varying(128) NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
---------------------------------
|
---------------------------------
|
||||||
-- Users
|
-- Users
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
insert into res_users (id,login,password,active,company_id,partner_id) VALUES (1,'admin','admin',true,1,1);
|
||||||
|
insert into ir_model_data (name,module,model,noupdate,res_id) VALUES ('user_root','base','res.users',true,1);
|
||||||
|
|
||||||
insert into res_users (id,login,password,active,company_id,partner_id,lang) values (1,'admin','admin',True,1,1,'en_US');
|
insert into res_partner (id, name, lang, company_id) VALUES (1, 'Your Company', 'en_US', 1);
|
||||||
insert into ir_model_data (name,module,model,noupdate,res_id) values ('user_root','base','res.users',True,1);
|
insert into ir_model_data (name,module,model,noupdate,res_id) VALUES ('main_partner','base','res.partner',true,1);
|
||||||
|
|
||||||
-- Compatibility purpose, to remove V6.0
|
insert into res_currency (id, name) VALUES (1, 'EUR');
|
||||||
insert into ir_model_data (name,module,model,noupdate,res_id) values ('user_admin','base','res.users',True,1);
|
insert into ir_model_data (name,module,model,noupdate,res_id) VALUES ('EUR','base','res.currency',true,1);
|
||||||
|
|
||||||
|
insert into res_company (id, name, partner_id, currency_id) VALUES (1, 'Your Company', 1, 1);
|
||||||
|
insert into ir_model_data (name,module,model,noupdate,res_id) VALUES ('main_company','base','res.company',true,1);
|
||||||
|
|
||||||
|
select setval('res_company_id_seq', 2);
|
||||||
select setval('res_users_id_seq', 2);
|
select setval('res_users_id_seq', 2);
|
||||||
|
select setval('res_partner_id_seq', 2);
|
||||||
|
select setval('res_currency_id_seq', 2);
|
|
@ -142,7 +142,7 @@ class res_users(osv.osv):
|
||||||
'id': fields.integer('ID'),
|
'id': fields.integer('ID'),
|
||||||
'login_date': fields.date('Latest connection', select=1),
|
'login_date': fields.date('Latest connection', select=1),
|
||||||
'partner_id': fields.many2one('res.partner', required=True,
|
'partner_id': fields.many2one('res.partner', required=True,
|
||||||
string='Related Partner', ondelete='cascade',
|
string='Related Partner', ondelete='restrict',
|
||||||
help='Partner-related data of the user'),
|
help='Partner-related data of the user'),
|
||||||
'login': fields.char('Login', size=64, required=True,
|
'login': fields.char('Login', size=64, required=True,
|
||||||
help="Used to log into the system"),
|
help="Used to log into the system"),
|
||||||
|
|
|
@ -110,15 +110,16 @@ class Registry(object):
|
||||||
and registers them in the registry.
|
and registers them in the registry.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
models_to_load = [] # need to preserve loading order
|
||||||
res = []
|
|
||||||
|
|
||||||
# Instantiate registered classes (via the MetaModel automatic discovery
|
# Instantiate registered classes (via the MetaModel automatic discovery
|
||||||
# or via explicit constructor call), and add them to the pool.
|
# or via explicit constructor call), and add them to the pool.
|
||||||
for cls in openerp.osv.orm.MetaModel.module_to_models.get(module.name, []):
|
for cls in openerp.osv.orm.MetaModel.module_to_models.get(module.name, []):
|
||||||
res.append(cls.create_instance(self, cr))
|
# models register themselves in self.models
|
||||||
|
model = cls.create_instance(self, cr)
|
||||||
return res
|
if model._name not in models_to_load:
|
||||||
|
# avoid double-loading models whose declaration is split
|
||||||
|
models_to_load.append(model._name)
|
||||||
|
return [self.models[m] for m in models_to_load]
|
||||||
|
|
||||||
def schedule_cron_jobs(self):
|
def schedule_cron_jobs(self):
|
||||||
""" Make the cron thread care about this registry/database jobs.
|
""" Make the cron thread care about this registry/database jobs.
|
||||||
|
|
|
@ -2890,15 +2890,15 @@ class BaseModel(object):
|
||||||
# usually because they could block deletion due to the FKs.
|
# usually because they could block deletion due to the FKs.
|
||||||
# So unless stated otherwise we default them to ondelete=cascade.
|
# So unless stated otherwise we default them to ondelete=cascade.
|
||||||
ondelete = ondelete or 'cascade'
|
ondelete = ondelete or 'cascade'
|
||||||
self._foreign_keys.append((self._table, source_field, dest_model._table, ondelete or 'set null'))
|
fk_def = (self._table, source_field, dest_model._table, ondelete or 'set null')
|
||||||
_schema.debug("Table '%s': added foreign key '%s' with definition=REFERENCES \"%s\" ON DELETE %s",
|
self._foreign_keys.add(fk_def)
|
||||||
self._table, source_field, dest_model._table, ondelete)
|
_schema.debug("Table '%s': added foreign key '%s' with definition=REFERENCES \"%s\" ON DELETE %s", *fk_def)
|
||||||
|
|
||||||
# unchecked version: for custom cases, such as m2m relationships
|
# unchecked version: for custom cases, such as m2m relationships
|
||||||
def _m2o_add_foreign_key_unchecked(self, source_table, source_field, dest_model, ondelete):
|
def _m2o_add_foreign_key_unchecked(self, source_table, source_field, dest_model, ondelete):
|
||||||
self._foreign_keys.append((source_table, source_field, dest_model._table, ondelete or 'set null'))
|
fk_def = (source_table, source_field, dest_model._table, ondelete or 'set null')
|
||||||
_schema.debug("Table '%s': added foreign key '%s' with definition=REFERENCES \"%s\" ON DELETE %s",
|
self._foreign_keys.add(fk_def)
|
||||||
source_table, source_field, dest_model._table, ondelete)
|
_schema.debug("Table '%s': added foreign key '%s' with definition=REFERENCES \"%s\" ON DELETE %s", *fk_def)
|
||||||
|
|
||||||
def _drop_constraint(self, cr, source_table, constraint_name):
|
def _drop_constraint(self, cr, source_table, constraint_name):
|
||||||
cr.execute("ALTER TABLE %s DROP CONSTRAINT %s" % (source_table,constraint_name))
|
cr.execute("ALTER TABLE %s DROP CONSTRAINT %s" % (source_table,constraint_name))
|
||||||
|
@ -2929,18 +2929,22 @@ class BaseModel(object):
|
||||||
cons, = constraints
|
cons, = constraints
|
||||||
if cons['ondelete_rule'] != POSTGRES_CONFDELTYPES.get((ondelete or 'set null').upper(), 'a')\
|
if cons['ondelete_rule'] != POSTGRES_CONFDELTYPES.get((ondelete or 'set null').upper(), 'a')\
|
||||||
or cons['foreign_table'] != dest_model._table:
|
or cons['foreign_table'] != dest_model._table:
|
||||||
|
# Wrong FK: drop it and recreate
|
||||||
_schema.debug("Table '%s': dropping obsolete FK constraint: '%s'",
|
_schema.debug("Table '%s': dropping obsolete FK constraint: '%s'",
|
||||||
source_table, cons['constraint_name'])
|
source_table, cons['constraint_name'])
|
||||||
self._drop_constraint(cr, source_table, cons['constraint_name'])
|
self._drop_constraint(cr, source_table, cons['constraint_name'])
|
||||||
self._m2o_add_foreign_key_checked(source_field, dest_model, ondelete)
|
else:
|
||||||
# else it's all good, nothing to do!
|
# it's all good, nothing to do!
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
# Multiple FKs found for the same field, drop them all, and re-create
|
# Multiple FKs found for the same field, drop them all, and re-create
|
||||||
for cons in constraints:
|
for cons in constraints:
|
||||||
_schema.debug("Table '%s': dropping duplicate FK constraints: '%s'",
|
_schema.debug("Table '%s': dropping duplicate FK constraints: '%s'",
|
||||||
source_table, cons['constraint_name'])
|
source_table, cons['constraint_name'])
|
||||||
self._drop_constraint(cr, source_table, cons['constraint_name'])
|
self._drop_constraint(cr, source_table, cons['constraint_name'])
|
||||||
self._m2o_add_foreign_key_checked(source_field, dest_model, ondelete)
|
|
||||||
|
# (re-)create the FK
|
||||||
|
self._m2o_add_foreign_key_checked(source_field, dest_model, ondelete)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2962,7 +2966,7 @@ class BaseModel(object):
|
||||||
_auto_end).
|
_auto_end).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self._foreign_keys = []
|
self._foreign_keys = set()
|
||||||
raise_on_invalid_object_name(self._name)
|
raise_on_invalid_object_name(self._name)
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
|
|
Loading…
Reference in New Issue