[FIX] ir_model_fields: fix and simplify update on custom fields

Avoid "patching" the registry, as this introduces inconsistencies (some field
attributes are lost).  Instead, proceed as follows:
 - update the definition of custom fields in database;
 - clear the corresponding cache on the registry (this was missing);
 - setup the models in registry (this reloads the custom models and fields);
 - update the database schema of the models based on the registry.

This makes the update of custom fields simpler and more robust.
This commit is contained in:
Raphael Collet 2015-10-01 15:26:07 +02:00
parent 3f31c378fa
commit cca7a7a051
1 changed files with 12 additions and 37 deletions

View File

@ -423,21 +423,8 @@ class ir_model_fields(osv.osv):
# if set, *one* column can be renamed here
column_rename = None
# field patches {model: {field_name: {prop_name: prop_value, ...}, ...}, ...}
patches = defaultdict(lambda: defaultdict(dict))
# static table of properties
model_props = [ # (our-name, fields.prop, set_fn)
('field_description', 'string', tools.ustr),
('required', 'required', bool),
('readonly', 'readonly', bool),
('domain', 'domain', eval),
('size', 'size', int),
('on_delete', 'ondelete', str),
('translate', 'translate', bool),
('select_level', 'index', lambda x: bool(int(x))),
('selection', 'selection', eval),
]
# names of the models to patch
patched_models = set()
if vals and ids:
checked_selection = False # need only check it once, so defer
@ -481,12 +468,7 @@ class ir_model_fields(osv.osv):
# We don't check the 'state', because it might come from the context
# (thus be set for multiple fields) and will be ignored anyway.
if obj is not None and field is not None:
# find out which properties (per model) we need to update
for field_name, prop_name, func in model_props:
if field_name in vals:
prop_value = func(vals[field_name])
if getattr(field, prop_name) != prop_value:
patches[obj][final_name][prop_name] = prop_value
patched_models.add(obj._name)
# These shall never be written (modified)
for column_name in ('model_id', 'model', 'state'):
@ -495,16 +477,17 @@ class ir_model_fields(osv.osv):
res = super(ir_model_fields,self).write(cr, user, ids, vals, context=context)
self.pool.clear_manual_fields()
if column_rename:
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 field in obj._fields and obj._columns
field = obj._pop_field(rename[1])
obj._add_field(rename[2], field)
if column_rename or patched_models:
# setup models, this will reload all manual fields in registry
self.pool.setup_models(cr, partial=(not self.pool.ready))
if patches:
if patched_models:
# We have to update _columns of the model(s) and then call their
# _auto_init to sync the db with the model. Hopefully, since write()
# was called earlier, they will be in-sync before the _auto_init.
@ -514,20 +497,12 @@ class ir_model_fields(osv.osv):
select=vals.get('select_level', '0'),
update_custom_fields=True,
)
for obj, model_patches in patches.iteritems():
for field_name, field_patches in model_patches.iteritems():
# update field properties, and adapt corresponding column
field = obj._fields[field_name]
attrs = dict(field._attrs, **field_patches)
obj._add_field(field_name, field.new(**attrs))
# update database schema
self.pool.setup_models(cr, partial=(not self.pool.ready))
for model_name in patched_models:
obj = self.pool[model_name]
obj._auto_init(cr, ctx)
obj._auto_end(cr, ctx) # actually create FKs!
if column_rename or patches:
if column_rename or patched_models:
RegistryManager.signal_registry_change(cr.dbname)
return res