[FIX] orm,registry: properly check m2o FKs during model update + fix some models...
authorOlivier Dony <odo@openerp.com>
Mon, 17 Dec 2012 21:46:45 +0000 (22:46 +0100)
committerOlivier Dony <odo@openerp.com>
Mon, 17 Dec 2012 21:46:45 +0000 (22:46 +0100)
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

openerp/addons/base/base.sql
openerp/addons/base/res/res_users.py
openerp/modules/registry.py
openerp/osv/orm.py

index 3fa847c..b1ddb2e 100644 (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_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);
 
-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_currency (id, name) VALUES (1, 'EUR');
+insert into ir_model_data (name,module,model,noupdate,res_id) VALUES ('EUR','base','res.currency',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_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);
\ No newline at end of file
index 41288c3..520f0f1 100644 (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"),
index afa8351..23dbc28 100644 (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.
index bfe5795..92eb8da 100644 (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 = {}