[FIX] ir_models: fix registry when we add/remove/modify a custom model/field

This commit is contained in:
Raphael Collet 2014-09-24 11:28:28 +02:00
parent 9696d76aec
commit 24f26c0eeb
2 changed files with 49 additions and 33 deletions

View 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):

View File

@ -478,6 +478,18 @@ class BaseModel(object):
# remove potential column that may be overridden by field
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