[FIX] ir_model: fix create/update/delete custom fields
Creating custom fields would crash on a model that has a related field without string. The crash was caused by the field not being set up, and method BaseModel._field_create() violating a non-null constraint on the field string. This has been fixed by setting up fields before updating ir_model_fields. Deleting a custom field could also cause trouble when that field is inherited in a child model. In that case, the registry was simply no longer consistent. The fix is to reload completely the registry. The modification of custom fields was not reflected on field objects. The fix applies changes on fields before updating columns accordingly.
This commit is contained in:
parent
d50d89566c
commit
3adbb49ec0
|
@ -204,7 +204,10 @@ class ir_model(osv.osv):
|
|||
vals['state']='manual'
|
||||
res = super(ir_model,self).create(cr, user, vals, context)
|
||||
if vals.get('state','base')=='manual':
|
||||
# add model in registry
|
||||
self.instanciate(cr, user, vals['model'], context)
|
||||
self.pool.setup_models(cr, partial=(not self.pool.ready))
|
||||
# update database schema
|
||||
model = self.pool[vals['model']]
|
||||
ctx = dict(context,
|
||||
field_name=vals['name'],
|
||||
|
@ -213,7 +216,6 @@ class ir_model(osv.osv):
|
|||
update_custom_fields=True)
|
||||
model._auto_init(cr, ctx)
|
||||
model._auto_end(cr, ctx) # actually create FKs!
|
||||
self.pool.setup_models(cr, partial=(not self.pool.ready))
|
||||
RegistryManager.signal_registry_change(cr.dbname)
|
||||
return res
|
||||
|
||||
|
@ -351,8 +353,11 @@ class ir_model_fields(osv.osv):
|
|||
self._drop_column(cr, user, ids, context)
|
||||
res = super(ir_model_fields, self).unlink(cr, user, ids, context)
|
||||
if not context.get(MODULE_UNINSTALL_FLAG):
|
||||
# The field we just deleted might have be inherited, and registry is
|
||||
# inconsistent in this case; therefore we reload the registry.
|
||||
cr.commit()
|
||||
self.pool.setup_models(cr, partial=(not self.pool.ready))
|
||||
api.Environment.reset()
|
||||
RegistryManager.new(cr.dbname)
|
||||
RegistryManager.signal_registry_change(cr.dbname)
|
||||
return res
|
||||
|
||||
|
@ -385,8 +390,11 @@ class ir_model_fields(osv.osv):
|
|||
cr.execute('SELECT * FROM ir_model_fields WHERE id=%s', (res,))
|
||||
self.pool.fields_by_model.setdefault(vals['model'], []).append(cr.dictfetchone())
|
||||
|
||||
# re-initialize model in registry
|
||||
model.__init__(self.pool, cr)
|
||||
#Added context to _auto_init for special treatment to custom field for select_level
|
||||
self.pool.setup_models(cr, partial=(not self.pool.ready))
|
||||
# update database schema
|
||||
model = self.pool[vals['model']]
|
||||
ctx = dict(context,
|
||||
field_name=vals['name'],
|
||||
field_state='manual',
|
||||
|
@ -394,7 +402,6 @@ class ir_model_fields(osv.osv):
|
|||
update_custom_fields=True)
|
||||
model._auto_init(cr, ctx)
|
||||
model._auto_end(cr, ctx) # actually create FKs!
|
||||
self.pool.setup_models(cr, partial=(not self.pool.ready))
|
||||
RegistryManager.signal_registry_change(cr.dbname)
|
||||
|
||||
return res
|
||||
|
@ -413,23 +420,24 @@ class ir_model_fields(osv.osv):
|
|||
if field.serialization_field_id and (field.name != vals['name']):
|
||||
raise except_orm(_('Error!'), _('Renaming sparse field "%s" is not allowed')%field.name)
|
||||
|
||||
column_rename = None # if set, *one* column can be renamed here
|
||||
models_patch = {} # structs of (obj, [(field, prop, change_to),..])
|
||||
# data to be updated on the orm model
|
||||
# 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),
|
||||
('domain', 'domain', eval),
|
||||
('size', 'size', int),
|
||||
('on_delete', 'ondelete', str),
|
||||
('translate', 'translate', bool),
|
||||
('selectable', 'selectable', bool),
|
||||
('select_level', 'select', int),
|
||||
('select_level', 'index', lambda x: bool(int(x))),
|
||||
('selection', 'selection', eval),
|
||||
]
|
||||
]
|
||||
|
||||
if vals and ids:
|
||||
checked_selection = False # need only check it once, so defer
|
||||
|
@ -472,14 +480,12 @@ 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:
|
||||
models_patch.setdefault(obj._name, (obj,[]))
|
||||
# find out which properties (per model) we need to update
|
||||
for field_name, field_property, set_fn in model_props:
|
||||
for field_name, prop_name, func in model_props:
|
||||
if field_name in vals:
|
||||
property_value = set_fn(vals[field_name])
|
||||
if getattr(obj._columns[item.name], field_property) != property_value:
|
||||
models_patch[obj._name][1].append((final_name, field_property, property_value))
|
||||
# our dict is ready here, but no properties are changed so far
|
||||
prop_value = func(vals[field_name])
|
||||
if getattr(obj._fields[item.name], prop_name) != prop_value:
|
||||
patches[obj][final_name][prop_name] = prop_value
|
||||
|
||||
# These shall never be written (modified)
|
||||
for column_name in ('model_id', 'model', 'state'):
|
||||
|
@ -496,24 +502,29 @@ class ir_model_fields(osv.osv):
|
|||
field = obj._pop_field(rename[1])
|
||||
obj._add_field(rename[2], field)
|
||||
|
||||
if models_patch:
|
||||
if patches:
|
||||
# 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.
|
||||
# Anything we don't update in _columns now will be reset from
|
||||
# the model into ir.model.fields (db).
|
||||
ctx = dict(context, select=vals.get('select_level', '0'),
|
||||
update_custom_fields=True)
|
||||
ctx = dict(context,
|
||||
select=vals.get('select_level', '0'),
|
||||
update_custom_fields=True,
|
||||
)
|
||||
|
||||
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)
|
||||
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
|
||||
obj._auto_init(cr, ctx)
|
||||
obj._auto_end(cr, ctx) # actually create FKs!
|
||||
|
||||
if column_rename or models_patch:
|
||||
if column_rename or patches:
|
||||
self.pool.setup_models(cr, partial=(not self.pool.ready))
|
||||
RegistryManager.signal_registry_change(cr.dbname)
|
||||
|
||||
|
|
Loading…
Reference in New Issue