[FIX] ir_models: fix registry when we add/remove/modify a custom model/field
authorRaphael Collet <rco@openerp.com>
Wed, 24 Sep 2014 09:28:28 +0000 (11:28 +0200)
committerRaphael Collet <rco@openerp.com>
Wed, 24 Sep 2014 13:30:37 +0000 (15:30 +0200)
openerp/addons/base/ir/ir_model.py
openerp/models.py

index 2007092..f1f2bfc 100644 (file)
@@ -26,9 +26,9 @@ import time
 import types
 
 import openerp
-import openerp.modules.registry
 from openerp import SUPERUSER_ID
-from openerp import tools
+from openerp import models, tools
+from openerp.modules.registry import RegistryManager
 from openerp.osv import fields, osv
 from openerp.osv.orm import BaseModel, Model, MAGIC_COLUMNS, except_orm
 from openerp.tools import config
@@ -181,8 +181,8 @@ class ir_model(osv.osv):
             # only reload pool for normal unlink. For module uninstall the
             # reload is done independently in openerp.modules.loading
             cr.commit() # must be committed before reloading registry in new cursor
-            openerp.modules.registry.RegistryManager.new(cr.dbname)
-            openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
+            RegistryManager.new(cr.dbname)
+            RegistryManager.signal_registry_change(cr.dbname)
 
         return res
 
@@ -205,8 +205,6 @@ class ir_model(osv.osv):
         if vals.get('state','base')=='manual':
             self.instanciate(cr, user, vals['model'], context)
             model = self.pool[vals['model']]
-            model._prepare_setup_fields(cr, SUPERUSER_ID)
-            model._setup_fields(cr, SUPERUSER_ID)
             ctx = dict(context,
                 field_name=vals['name'],
                 field_state='manual',
@@ -214,25 +212,25 @@ class ir_model(osv.osv):
                 update_custom_fields=True)
             model._auto_init(cr, ctx)
             model._auto_end(cr, ctx) # actually create FKs!
-            openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
+            self.pool.setup_models(cr, partial=(not self.pool.ready))
+            RegistryManager.signal_registry_change(cr.dbname)
         return res
 
     def instanciate(self, cr, user, model, context=None):
-        class x_custom_model(osv.osv):
-            _custom = True
         if isinstance(model, unicode):
             model = model.encode('utf-8')
-        x_custom_model._name = model
-        x_custom_model._module = False
-        a = x_custom_model._build_model(self.pool, cr)
-        if not a._columns:
-            x_name = 'id'
-        elif 'x_name' in a._columns.keys():
-            x_name = 'x_name'
-        else:
-            x_name = a._columns.keys()[0]
-        x_custom_model._rec_name = x_name
-        a._rec_name = x_name
+
+        class CustomModel(models.Model):
+            _name = model
+            _module = False
+            _custom = True
+
+        obj = CustomModel._build_model(self.pool, cr)
+        obj._rec_name = CustomModel._rec_name = (
+            'x_name' if 'x_name' in obj._columns else
+            list(obj._columns)[0] if obj._columns else
+            'id'
+        )
 
 class ir_model_fields(osv.osv):
     _name = 'ir.model.fields'
@@ -329,15 +327,15 @@ class ir_model_fields(osv.osv):
             column_name = cr.fetchone()
             if column_name and (result and result[0] == 'r'):
                 cr.execute('ALTER table "%s" DROP column "%s" cascade' % (model._table, field.name))
-            model._columns.pop(field.name, None)
-
             # remove m2m relation table for custom fields
             # we consider the m2m relation is only one way as it's not possible
             # to specify the relation table in the interface for custom fields
             # TODO master: maybe use ir.model.relations for custom fields
             if field.state == 'manual' and field.ttype == 'many2many':
-                rel_name = self.pool[field.model]._all_columns[field.name].column._rel
+                rel_name = model._fields[field.name].relation
                 cr.execute('DROP table "%s"' % (rel_name))
+            model._pop_field(field.name)
+
         return True
 
     def unlink(self, cr, user, ids, context=None):
@@ -353,7 +351,8 @@ class ir_model_fields(osv.osv):
         res = super(ir_model_fields, self).unlink(cr, user, ids, context)
         if not context.get(MODULE_UNINSTALL_FLAG):
             cr.commit()
-            openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
+            self.pool.setup_models(cr, partial=(not self.pool.ready))
+            RegistryManager.signal_registry_change(cr.dbname)
         return res
 
     def create(self, cr, user, vals, context=None):
@@ -384,10 +383,8 @@ class ir_model_fields(osv.osv):
                 if self.pool.fields_by_model is not None:
                     cr.execute('SELECT * FROM ir_model_fields WHERE id=%s', (res,))
                     self.pool.fields_by_model.setdefault(vals['model'], []).append(cr.dictfetchone())
-                model.__init__(self.pool, cr)
-                model._prepare_setup_fields(cr, SUPERUSER_ID)
-                model._setup_fields(cr, SUPERUSER_ID)
 
+                model.__init__(self.pool, cr)
                 #Added context to _auto_init for special treatment to custom field for select_level
                 ctx = dict(context,
                     field_name=vals['name'],
@@ -396,7 +393,8 @@ class ir_model_fields(osv.osv):
                     update_custom_fields=True)
                 model._auto_init(cr, ctx)
                 model._auto_end(cr, ctx) # actually create FKs!
-                openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
+                self.pool.setup_models(cr, partial=(not self.pool.ready))
+                RegistryManager.signal_registry_change(cr.dbname)
 
         return res
 
@@ -490,11 +488,12 @@ class ir_model_fields(osv.osv):
         res = super(ir_model_fields,self).write(cr, user, ids, vals, context=context)
 
         if column_rename:
-            cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO "%s"' % column_rename[1])
+            obj, rename = column_rename
+            cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO "%s"' % rename)
             # This is VERY risky, but let us have this feature:
-            # we want to change the key of column in obj._columns dict
-            col = column_rename[0]._columns.pop(column_rename[1][1]) # take object out, w/o copy
-            column_rename[0]._columns[column_rename[1][2]] = col
+            # we want to change the key of field in obj._fields and obj._columns
+            field = obj._pop_field(rename[1])
+            obj._add_field(rename[2], field)
 
         if models_patch:
             # We have to update _columns of the model(s) and then call their
@@ -507,11 +506,16 @@ class ir_model_fields(osv.osv):
 
             for __, patch_struct in models_patch.items():
                 obj = patch_struct[0]
+                # TODO: update new-style fields accordingly
                 for col_name, col_prop, val in patch_struct[1]:
                     setattr(obj._columns[col_name], col_prop, val)
                 obj._auto_init(cr, ctx)
                 obj._auto_end(cr, ctx) # actually create FKs!
-            openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
+
+        if column_rename or models_patch:
+            self.pool.setup_models(cr, partial=(not self.pool.ready))
+            RegistryManager.signal_registry_change(cr.dbname)
+
         return res
 
 class ir_model_constraint(Model):
index 6647502..360c220 100644 (file)
@@ -479,6 +479,18 @@ class BaseModel(object):
             cls._columns.pop(name, None)
 
     @classmethod
+    def _pop_field(cls, name):
+        """ Remove the field with the given `name` from the model.
+            This method should only be used for manual fields.
+        """
+        field = cls._fields.pop(name)
+        cls._columns.pop(name, None)
+        cls._all_columns.pop(name, None)
+        if hasattr(cls, name):
+            delattr(cls, name)
+        return field
+
+    @classmethod
     def _add_magic_fields(cls):
         """ Introduce magic fields on the current class