[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:
Olivier Dony 2012-12-17 22:46:45 +01:00
parent f827de7049
commit 9f77d2e2f4
4 changed files with 54 additions and 26 deletions

View File

@ -149,7 +149,6 @@ CREATE TABLE res_users (
active boolean default True,
login varchar(64) NOT NULL UNIQUE,
password varchar(64) default null,
lang varchar(64) default '',
-- No FK references below, will be added later by ORM
-- (when the destination rows exist)
company_id int,
@ -316,13 +315,29 @@ CREATE TABLE ir_module_module_dependency (
primary key(id)
);
CREATE TABLE res_company (
CREATE TABLE res_partner (
id serial NOT NULL,
name character varying(64) not null,
parent_id integer references res_company on delete set null,
name character varying(128),
lang varchar(64),
company_id int,
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 (
id serial PRIMARY KEY,
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,
model integer NOT NULL references ir_model on delete restrict,
name character varying(128) NOT NULL
);
);
---------------------------------
-- 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 ir_model_data (name,module,model,noupdate,res_id) values ('user_root','base','res.users',True,1);
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 ('main_partner','base','res.partner',true,1);
-- Compatibility purpose, to remove V6.0
insert into ir_model_data (name,module,model,noupdate,res_id) values ('user_admin','base','res.users',True,1);
insert into res_currency (id, name) VALUES (1, 'EUR');
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_partner_id_seq', 2);
select setval('res_currency_id_seq', 2);

View File

@ -142,7 +142,7 @@ class res_users(osv.osv):
'id': fields.integer('ID'),
'login_date': fields.date('Latest connection', select=1),
'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'),
'login': fields.char('Login', size=64, required=True,
help="Used to log into the system"),

View File

@ -110,15 +110,16 @@ class Registry(object):
and registers them in the registry.
"""
res = []
models_to_load = [] # need to preserve loading order
# Instantiate registered classes (via the MetaModel automatic discovery
# or via explicit constructor call), and add them to the pool.
for cls in openerp.osv.orm.MetaModel.module_to_models.get(module.name, []):
res.append(cls.create_instance(self, cr))
return res
# models register themselves in self.models
model = cls.create_instance(self, cr)
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):
""" Make the cron thread care about this registry/database jobs.

View File

@ -2890,15 +2890,15 @@ class BaseModel(object):
# usually because they could block deletion due to the FKs.
# So unless stated otherwise we default them to ondelete=cascade.
ondelete = ondelete or 'cascade'
self._foreign_keys.append((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._table, source_field, dest_model._table, ondelete)
fk_def = (self._table, source_field, dest_model._table, ondelete or 'set null')
self._foreign_keys.add(fk_def)
_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
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'))
_schema.debug("Table '%s': added foreign key '%s' with definition=REFERENCES \"%s\" ON DELETE %s",
source_table, source_field, dest_model._table, ondelete)
fk_def = (source_table, source_field, dest_model._table, ondelete or 'set null')
self._foreign_keys.add(fk_def)
_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):
cr.execute("ALTER TABLE %s DROP CONSTRAINT %s" % (source_table,constraint_name))
@ -2929,18 +2929,22 @@ class BaseModel(object):
cons, = constraints
if cons['ondelete_rule'] != POSTGRES_CONFDELTYPES.get((ondelete or 'set null').upper(), 'a')\
or cons['foreign_table'] != dest_model._table:
# Wrong FK: drop it and recreate
_schema.debug("Table '%s': dropping obsolete FK constraint: '%s'",
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 it's all good, nothing to do!
else:
# it's all good, nothing to do!
return
else:
# Multiple FKs found for the same field, drop them all, and re-create
for cons in constraints:
_schema.debug("Table '%s': dropping duplicate FK constraints: '%s'",
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).
"""
self._foreign_keys = []
self._foreign_keys = set()
raise_on_invalid_object_name(self._name)
if context is None:
context = {}