[FIX]: uninstall module.
Remove foreign key references. Remove sql constraint . Remove workflow activity and transition based on deleted cascade. Drop ir model fields columns and drop table. bzr revid: atp@tinyerp.com-20120309124753-c4yzeoij5p2fmhgg
This commit is contained in:
parent
c86917ea38
commit
180a23c7cc
|
@ -138,15 +138,21 @@ class ir_model(osv.osv):
|
|||
def _drop_table(self, cr, uid, ids, context=None):
|
||||
for model in self.browse(cr, uid, ids, context):
|
||||
model_pool = self.pool.get(model.model)
|
||||
if getattr(model_pool, '_auto', True) and not model.osv_memory:
|
||||
cr.execute("DROP table %s cascade" % model_pool._table)
|
||||
# this test should be removed, but check if drop view instead of drop table
|
||||
# just check if table or view exists
|
||||
cr.execute("select relkind from pg_class where relname=%s", (model_pool._table,))
|
||||
result = cr.fetchone()
|
||||
if result and result[0] == 'v':
|
||||
cr.execute("DROP view %s" % (model_pool._table,))
|
||||
elif result and result[0] == 'r':
|
||||
cr.execute("DROP TABLE %s" % (model_pool._table,))
|
||||
return True
|
||||
|
||||
def unlink(self, cr, user, ids, context=None):
|
||||
# for model in self.browse(cr, user, ids, context):
|
||||
# if model.state != 'manual':
|
||||
# raise except_orm(_('Error'), _("You can not remove the model '%s' !") %(model.name,))
|
||||
# self._drop_table(cr, user, ids, context)
|
||||
self._drop_table(cr, user, ids, context)
|
||||
res = super(ir_model, self).unlink(cr, user, ids, context)
|
||||
pooler.restart_pool(cr.dbname)
|
||||
return res
|
||||
|
@ -273,11 +279,15 @@ class ir_model_fields(osv.osv):
|
|||
]
|
||||
|
||||
def _drop_column(self, cr, uid, ids, context=None):
|
||||
for field in self.browse(cr, uid, ids, context):
|
||||
model = self.pool.get(field.model)
|
||||
if not field.model.osv_memory and getattr(model, '_auto', True):
|
||||
cr.execute("ALTER table %s DROP column %s" % (model._table, field.name))
|
||||
model._columns.pop(field.name, None)
|
||||
field = self.browse(cr, uid, ids, context)
|
||||
model = self.pool.get(field.model)
|
||||
cr.execute("select relkind from pg_class where relname=%s", (model._table,))
|
||||
result = cr.fetchone()[0]
|
||||
cr.execute("SELECT column_name FROM information_schema.columns WHERE table_name ='%s'and column_name='%s'"%(model._table, field.name))
|
||||
column_name = cr.fetchone()
|
||||
if column_name and result == 'r':
|
||||
cr.execute("ALTER table %s DROP column %s cascade" % (model._table, field.name))
|
||||
model._columns.pop(field.name, None)
|
||||
return True
|
||||
|
||||
def unlink(self, cr, user, ids, context=None):
|
||||
|
@ -630,7 +640,6 @@ class ir_model_data(osv.osv):
|
|||
def __init__(self, pool, cr):
|
||||
osv.osv.__init__(self, pool, cr)
|
||||
self.doinit = True
|
||||
|
||||
# also stored in pool to avoid being discarded along with this osv instance
|
||||
if getattr(pool, 'model_data_reference_ids', None) is None:
|
||||
self.pool.model_data_reference_ids = {}
|
||||
|
@ -682,7 +691,6 @@ class ir_model_data(osv.osv):
|
|||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
""" Regular unlink method, but make sure to clear the caches. """
|
||||
self._pre_process_unlink(cr, uid, ids, context)
|
||||
self._get_id.clear_cache(self)
|
||||
self.get_object_reference.clear_cache(self)
|
||||
return super(ir_model_data,self).unlink(cr, uid, ids, context=context)
|
||||
|
@ -691,10 +699,8 @@ class ir_model_data(osv.osv):
|
|||
model_obj = self.pool.get(model)
|
||||
if not context:
|
||||
context = {}
|
||||
|
||||
# records created during module install should result in res.log entries that are already read!
|
||||
context = dict(context, res_log_read=True)
|
||||
|
||||
if xml_id and ('.' in xml_id):
|
||||
assert len(xml_id.split('.'))==2, _("'%s' contains too many dots. XML ids should not contain dots ! These are used to refer to other modules data, as in module.reference_id") % (xml_id)
|
||||
module, xml_id = xml_id.split('.')
|
||||
|
@ -807,41 +813,90 @@ class ir_model_data(osv.osv):
|
|||
def _pre_process_unlink(self, cr, uid, ids, context=None):
|
||||
wkf_todo = []
|
||||
to_unlink = []
|
||||
to_drop_table = []
|
||||
ids.sort()
|
||||
ids.reverse()
|
||||
for data in self.browse(cr, uid, ids, context):
|
||||
model = data.model
|
||||
res_id = data.res_id
|
||||
model_obj = self.pool.get(model)
|
||||
if str(data.name).startswith('constraint_'):
|
||||
cr.execute('ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model_obj._table,data.name[11:]),)
|
||||
_logger.info('Drop CONSTRAINT %s@%s', data.name[11:], model)
|
||||
name = data.name
|
||||
if str(name).startswith('foreign_key_'):
|
||||
name = name[12:]
|
||||
# test if constraint exists
|
||||
cr.execute('select conname from pg_constraint where contype=%s and conname=%s',('f', name),)
|
||||
if cr.fetchall():
|
||||
cr.execute('ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model,name),)
|
||||
_logger.info('Drop CONSTRAINT %s@%s', name, model)
|
||||
continue
|
||||
to_unlink.append((model,res_id))
|
||||
|
||||
if str(name).startswith('table_'):
|
||||
cr.execute("SELECT table_name FROM information_schema.tables WHERE table_name='%s'"%(name[6:]))
|
||||
column_name = cr.fetchone()
|
||||
if column_name:
|
||||
to_drop_table.append(name[6:])
|
||||
continue
|
||||
|
||||
if str(name).startswith('constraint_'):
|
||||
# test if constraint exists
|
||||
cr.execute('select conname from pg_constraint where contype=%s and conname=%s',('u', name),)
|
||||
if cr.fetchall():
|
||||
cr.execute('ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model_obj._table,name[11:]),)
|
||||
_logger.info('Drop CONSTRAINT %s@%s', name[11:], model)
|
||||
continue
|
||||
|
||||
to_unlink.append((model, res_id))
|
||||
if model=='workflow.activity':
|
||||
cr.execute('select res_type,res_id from wkf_instance where id IN (select inst_id from wkf_workitem where act_id=%s)', (res_id,))
|
||||
wkf_todo.extend(cr.fetchall())
|
||||
cr.execute("update wkf_transition set condition='True', group_id=NULL, signal=NULL,act_to=act_from,act_from=%s where act_to=%s", (res_id,res_id))
|
||||
cr.execute("delete from wkf_transition where act_to=%s", (res_id,))
|
||||
|
||||
|
||||
for model,res_id in wkf_todo:
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
wf_service.trg_write(uid, model, res_id, cr)
|
||||
try:
|
||||
wf_service.trg_write(uid, model, res_id, cr)
|
||||
except:
|
||||
_logger.info('Unable to process workflow %s@%s', res_id, model)
|
||||
|
||||
#cr.commit()
|
||||
if not config.get('import_partial'):
|
||||
for (model, res_id) in to_unlink:
|
||||
if self.pool.get(model):
|
||||
_logger.info('Deleting %s@%s', res_id, model)
|
||||
res_ids = self.pool.get(model).search(cr, uid, [('id', '=', res_id)])
|
||||
if res_ids:
|
||||
self.pool.get(model).unlink(cr, uid, [res_id])
|
||||
cr.commit()
|
||||
# except Exception:
|
||||
# cr.rollback()
|
||||
# _logger.warning(
|
||||
# 'Could not delete obsolete record with id: %d of model %s\n'
|
||||
## 'There should be some relation that points to this resource\n'
|
||||
# 'You should manually fix this and restart with --update=module',
|
||||
# res_id, model)
|
||||
# drop relation .table
|
||||
for model in to_drop_table:
|
||||
cr.execute('DROP TABLE %s cascade'% (model),)
|
||||
_logger.info('Dropping table %s', model)
|
||||
|
||||
for (model, res_id) in to_unlink:
|
||||
if model in ('ir.model','ir.model.fields', 'ir.model.data'):
|
||||
continue
|
||||
model_ids = self.search(cr, uid, [('model', '=', model),('res_id', '=', res_id)])
|
||||
if len(model_ids) > 1:
|
||||
# if others module have defined this record, we do not delete it
|
||||
continue
|
||||
_logger.info('Deleting %s@%s', res_id, model)
|
||||
try:
|
||||
self.pool.get(model).unlink(cr, uid, res_id)
|
||||
except:
|
||||
_logger.info('Unable to delete %s@%s', res_id, model)
|
||||
cr.commit()
|
||||
|
||||
for (model, res_id) in to_unlink:
|
||||
if model not in ('ir.model.fields',):
|
||||
continue
|
||||
model_ids = self.search(cr, uid, [('model', '=', model),('res_id', '=', res_id)])
|
||||
if len(model_ids) > 1:
|
||||
# if others module have defined this record, we do not delete it
|
||||
continue
|
||||
_logger.info('Deleting %s@%s', res_id, model)
|
||||
self.pool.get(model).unlink(cr, uid, res_id)
|
||||
|
||||
for (model, res_id) in to_unlink:
|
||||
if model not in ('ir.model',):
|
||||
continue
|
||||
model_ids = self.search(cr, uid, [('model', '=', model),('res_id', '=', res_id)])
|
||||
if len(model_ids) > 1:
|
||||
# if others module have defined this record, we do not delete it
|
||||
continue
|
||||
_logger.info('Deleting %s@%s', res_id, model)
|
||||
self.pool.get(model).unlink(cr, uid, [res_id])
|
||||
cr.commit()
|
||||
|
||||
def _process_end(self, cr, uid, modules):
|
||||
""" Clear records removed from updated module data.
|
||||
|
@ -851,8 +906,6 @@ class ir_model_data(osv.osv):
|
|||
and a module in ir_model_data and noupdate set to false, but not
|
||||
present in self.loads.
|
||||
"""
|
||||
|
||||
|
||||
if not modules:
|
||||
return True
|
||||
modules = list(modules)
|
||||
|
@ -871,9 +924,6 @@ class ir_model_data(osv.osv):
|
|||
_logger.info('Deleting %s@%s', res_id, model)
|
||||
self.pool.get(model).unlink(cr, uid, [res_id])
|
||||
|
||||
# cr.commit()
|
||||
|
||||
|
||||
ir_model_data()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -194,7 +194,7 @@ class wkf_workitem(osv.osv):
|
|||
_log_access = False
|
||||
_rec_name = 'state'
|
||||
_columns = {
|
||||
'act_id': fields.many2one('workflow.activity', 'Activity', required=True, ondelete="restrict", select=True),
|
||||
'act_id': fields.many2one('workflow.activity', 'Activity', required=True, ondelete="cascade", select=True),
|
||||
'wkf_id': fields.related('act_id','wkf_id', type='many2one', relation='workflow', string='Workflow'),
|
||||
'subflow_id': fields.many2one('workflow.instance', 'Subflow', ondelete="cascade", select=True),
|
||||
'inst_id': fields.many2one('workflow.instance', 'Instance', required=True, ondelete="cascade", select=True),
|
||||
|
|
|
@ -343,7 +343,6 @@ class module(osv.osv):
|
|||
# Mark them to be installed.
|
||||
if to_install_ids:
|
||||
self.button_install(cr, uid, to_install_ids, context=context)
|
||||
|
||||
return dict(ACTION_DICT, name=_('Install'))
|
||||
|
||||
def button_immediate_install(self, cr, uid, ids, context=None):
|
||||
|
@ -377,11 +376,20 @@ class module(osv.osv):
|
|||
return True
|
||||
|
||||
def module_uninstall(self, cr, uid, ids, context=None):
|
||||
|
||||
# you have to uninstall in the right order, not all modules at the same time
|
||||
|
||||
model_data = self.pool.get('ir.model.data')
|
||||
remove_modules = map(lambda x: x.name, self.browse(cr, uid, ids, context))
|
||||
|
||||
data_ids = model_data.search(cr, uid, [('module', 'in', remove_modules)])
|
||||
|
||||
model_data._pre_process_unlink(cr, uid, data_ids, context)
|
||||
model_data.unlink(cr, uid, data_ids, context)
|
||||
|
||||
self.write(cr, uid, ids, {'state': 'uninstalled'})
|
||||
|
||||
# should we call process_end istead of loading, or both ?
|
||||
return True
|
||||
|
||||
def button_uninstall(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -97,12 +97,10 @@ class base_module_upgrade(osv.osv_memory):
|
|||
raise osv.except_osv(_('Unmet dependency !'), _('Following modules are not installed or unknown: %s') % ('\n\n' + '\n'.join(unmet_packages)))
|
||||
mod_obj.download(cr, uid, ids, context=context)
|
||||
cr.commit()
|
||||
|
||||
# process to remove modules
|
||||
remove_module_ids = mod_obj.search(cr, uid, [('state', 'in', ['to remove'])])
|
||||
mod_obj.module_uninstall(cr, uid, remove_module_ids, context)
|
||||
|
||||
|
||||
_db, pool = pooler.restart_pool(cr.dbname, update_module=True)
|
||||
|
||||
id2 = data_obj._get_id(cr, uid, 'base', 'view_base_module_upgrade_install')
|
||||
|
|
|
@ -107,21 +107,6 @@ class groups(osv.osv):
|
|||
aid.write({'groups_id': [(4, gid)]})
|
||||
return gid
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
group_users = []
|
||||
for record in self.read(cr, uid, ids, ['users'], context=context):
|
||||
if record['users']:
|
||||
group_users.extend(record['users'])
|
||||
if group_users:
|
||||
user_names = [user.name for user in self.pool.get('res.users').browse(cr, uid, group_users, context=context)]
|
||||
user_names = list(set(user_names))
|
||||
if len(user_names) >= 5:
|
||||
user_names = user_names[:5] + ['...']
|
||||
raise osv.except_osv(_('Warning !'),
|
||||
_('Group(s) cannot be deleted, because some user(s) still belong to them: %s !') % \
|
||||
', '.join(user_names))
|
||||
return super(groups, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
def get_extended_interface_group(self, cr, uid, context=None):
|
||||
data_obj = self.pool.get('ir.model.data')
|
||||
extended_group_data_id = data_obj._get_id(cr, uid, 'base', 'group_extended')
|
||||
|
|
|
@ -374,13 +374,12 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
|||
tools.config[kind] = {}
|
||||
|
||||
cr.commit()
|
||||
if update_module:
|
||||
# if update_module:
|
||||
# Remove records referenced from ir_model_data for modules to be
|
||||
# removed (and removed the references from ir_model_data).
|
||||
#cr.execute("select id,name from ir_module_module where state=%s", ('to remove',))
|
||||
#remove_modules = map(lambda x: x['name'], cr.dictfetchall())
|
||||
# Cleanup orphan records
|
||||
#print "pooler", pool.get('mrp.bom')
|
||||
#pool.get('ir.model.data')._process_end(cr, 1, remove_modules, noupdate=None)
|
||||
# for mod_id, mod_name in cr.fetchall():
|
||||
# cr.execute('select model,res_id from ir_model_data where noupdate=%s and module=%s order by id desc', (False, mod_name,))
|
||||
|
@ -398,20 +397,20 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
|||
# (child) menu item, ir_values, or ir_model_data.
|
||||
# This code could be a method of ir_ui_menu.
|
||||
# TODO: remove menu without actions of children
|
||||
while True:
|
||||
cr.execute('''delete from
|
||||
ir_ui_menu
|
||||
where
|
||||
(id not IN (select parent_id from ir_ui_menu where parent_id is not null))
|
||||
and
|
||||
(id not IN (select res_id from ir_values where model='ir.ui.menu'))
|
||||
and
|
||||
(id not IN (select res_id from ir_model_data where model='ir.ui.menu'))''')
|
||||
cr.commit()
|
||||
if not cr.rowcount:
|
||||
break
|
||||
else:
|
||||
_logger.info('removed %d unused menus', cr.rowcount)
|
||||
# while True:
|
||||
# cr.execute('''delete from
|
||||
# ir_ui_menu
|
||||
# where
|
||||
# (id not IN (select parent_id from ir_ui_menu where parent_id is not null))
|
||||
# and
|
||||
# (id not IN (select res_id from ir_values where model='ir.ui.menu'))
|
||||
# and
|
||||
# (id not IN (select res_id from ir_model_data where model='ir.ui.menu'))''')
|
||||
# cr.commit()
|
||||
# if not cr.rowcount:
|
||||
# break
|
||||
# else:
|
||||
# _logger.info('removed %d unused menus', cr.rowcount)
|
||||
|
||||
# Pretend that modules to be removed are actually uninstalled.
|
||||
#cr.execute("update ir_module_module set state=%s where state=%s", ('uninstalled', 'to remove',))
|
||||
|
|
|
@ -904,6 +904,7 @@ class BaseModel(object):
|
|||
# If new class defines a constraint with
|
||||
# same function name, we let it override
|
||||
# the old one.
|
||||
|
||||
new[c2] = c
|
||||
exist = True
|
||||
break
|
||||
|
@ -2760,7 +2761,6 @@ class BaseModel(object):
|
|||
update_custom_fields = context.get('update_custom_fields', False)
|
||||
self._field_create(cr, context=context)
|
||||
create = not self._table_exist(cr)
|
||||
|
||||
if getattr(self, '_auto', True):
|
||||
|
||||
if create:
|
||||
|
@ -3029,11 +3029,16 @@ class BaseModel(object):
|
|||
|
||||
return todo_end
|
||||
|
||||
|
||||
def _auto_end(self, cr, context=None):
|
||||
""" Create the foreign keys recorded by _auto_init. """
|
||||
for t, k, r, d in self._foreign_keys:
|
||||
cr.execute('ALTER TABLE "%s" ADD FOREIGN KEY ("%s") REFERENCES "%s" ON DELETE %s' % (t, k, r, d))
|
||||
name_id = "foreign_key_"+t+"_"+k+"_fkey"
|
||||
cr.execute('select * from ir_model_data where name=%s and module=%s', (name_id, self._module))
|
||||
if not cr.rowcount:
|
||||
cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module, model) VALUES (%s, now(), now(), %s, %s)", \
|
||||
(name_id, self._module, t)
|
||||
)
|
||||
cr.commit()
|
||||
del self._foreign_keys
|
||||
|
||||
|
@ -3112,6 +3117,7 @@ class BaseModel(object):
|
|||
|
||||
def _o2m_raise_on_missing_reference(self, cr, f):
|
||||
# TODO this check should be a method on fields.one2many.
|
||||
|
||||
other = self.pool.get(f._obj)
|
||||
if other:
|
||||
# TODO the condition could use fields_get_keys().
|
||||
|
@ -3119,7 +3125,6 @@ class BaseModel(object):
|
|||
if f._fields_id not in other._inherit_fields.keys():
|
||||
raise except_orm('Programming Error', ("There is no reference field '%s' found for '%s'") % (f._fields_id, f._obj,))
|
||||
|
||||
|
||||
def _m2m_raise_or_create_relation(self, cr, f):
|
||||
m2m_tbl, col1, col2 = f._sql_names(self)
|
||||
cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (m2m_tbl,))
|
||||
|
@ -3129,7 +3134,14 @@ class BaseModel(object):
|
|||
dest_model = self.pool.get(f._obj)
|
||||
ref = dest_model._table
|
||||
cr.execute('CREATE TABLE "%s" ("%s" INTEGER NOT NULL, "%s" INTEGER NOT NULL, UNIQUE("%s","%s")) WITH OIDS' % (m2m_tbl, col1, col2, col1, col2))
|
||||
|
||||
#create many2many references
|
||||
name_id = 'table_'+m2m_tbl
|
||||
cr.execute('select * from ir_model_data where name=%s and module=%s', (name_id, self._module))
|
||||
if not cr.rowcount:
|
||||
cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module, model) VALUES (%s, now(), now(), %s, %s)", \
|
||||
(name_id, self._module, self._name)
|
||||
)
|
||||
# self.pool.get('ir.model.data')._update(cr, 1, self._name, self._module, {}, 'table_'+m2m_tbl, store=True, noupdate=False, mode='init', res_id=False, context=None)
|
||||
# create foreign key references with ondelete=cascade, unless the targets are SQL views
|
||||
cr.execute("SELECT relkind FROM pg_class WHERE relkind IN ('v') AND relname=%s", (ref,))
|
||||
if not cr.fetchall():
|
||||
|
@ -3157,7 +3169,6 @@ class BaseModel(object):
|
|||
|
||||
cr.execute("SELECT conname, pg_catalog.pg_get_constraintdef(oid, true) as condef FROM pg_constraint where conname=%s", (conname,))
|
||||
existing_constraints = cr.dictfetchall()
|
||||
|
||||
sql_actions = {
|
||||
'drop': {
|
||||
'execute': False,
|
||||
|
@ -3198,11 +3209,10 @@ class BaseModel(object):
|
|||
_schema.debug(sql_action['msg_ok'])
|
||||
name_id = 'constraint_'+ conname
|
||||
cr.execute('select * from ir_model_data where name=%s and module=%s', (name_id, module))
|
||||
if not cr.rowcount:
|
||||
if not cr.rowcount:
|
||||
cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module, model) VALUES (%s, now(), now(), %s, %s)", \
|
||||
(name_id, module, self._name)
|
||||
)
|
||||
#
|
||||
except:
|
||||
_schema.warning(sql_action['msg_err'])
|
||||
cr.rollback()
|
||||
|
|
Loading…
Reference in New Issue