[IMP] uninstall: use a dedicated table for many2many relationships instead of overlaoding ir.model.data.
bzr revid: vmt@openerp.com-20120601153449-uj2sl7qjzz3s7xnj
This commit is contained in:
parent
d790650bfb
commit
eadd5e0a0a
|
@ -347,9 +347,8 @@ CREATE TABLE ir_model_data (
|
|||
res_id integer, primary key(id)
|
||||
);
|
||||
|
||||
-- Records foreign keys and constraints
|
||||
-- installed by a module (so they can be removed them when the module is
|
||||
-- uninstalled).:
|
||||
-- Records foreign keys and constraints installed by a module (so they can be
|
||||
-- removed them when the module is uninstalled):
|
||||
-- - for a foreign key: type 'f',
|
||||
-- - for a constraint: type 'u'.
|
||||
CREATE TABLE ir_model_constraint (
|
||||
|
@ -366,6 +365,21 @@ CREATE TABLE ir_model_constraint (
|
|||
name character varying(128) NOT NULL
|
||||
);
|
||||
|
||||
-- Records relation tables (i.e. implementing many2many) installed by a module
|
||||
-- (so they can be removed them when the module is uninstalled).
|
||||
CREATE TABLE ir_model_relation (
|
||||
id serial NOT NULL,
|
||||
create_uid integer,
|
||||
create_date timestamp without time zone,
|
||||
write_date timestamp without time zone,
|
||||
write_uid integer,
|
||||
date_init timestamp without time zone,
|
||||
date_update timestamp without time zone,
|
||||
module integer NOT NULL references ir_module_module on delete restrict,
|
||||
model integer NOT NULL references ir_model on delete restrict,
|
||||
name character varying(128) NOT NULL
|
||||
);
|
||||
|
||||
---------------------------------
|
||||
-- Users
|
||||
---------------------------------
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
import ir_model
|
||||
import ir_model_constraint
|
||||
import ir_model_relation
|
||||
import ir_sequence
|
||||
import ir_needaction
|
||||
import ir_ui_menu
|
||||
|
|
|
@ -29,7 +29,7 @@ from openerp import netsvc, pooler, tools
|
|||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
from openerp.tools import config
|
||||
from openerp.tools.translate import _
|
||||
from openerp.osv.orm import except_orm, browse_record, EXT_ID_PREFIX_M2M_TABLE
|
||||
from openerp.osv.orm import except_orm, browse_record
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -847,7 +847,6 @@ class ir_model_data(osv.osv):
|
|||
ids_set = set(ids)
|
||||
wkf_todo = []
|
||||
to_unlink = []
|
||||
to_drop_table = []
|
||||
ids.sort()
|
||||
ids.reverse()
|
||||
for data in self.browse(cr, uid, ids, context):
|
||||
|
@ -856,21 +855,6 @@ class ir_model_data(osv.osv):
|
|||
model_obj = self.pool.get(model)
|
||||
name = tools.ustr(data.name)
|
||||
|
||||
if name.startswith(EXT_ID_PREFIX_M2M_TABLE):
|
||||
# double-check we are really going to delete all the owners of this schema element
|
||||
cr.execute("""SELECT id from ir_model_data where name = %s and res_id IS NULL""", (data.name,))
|
||||
external_ids = [x[0] for x in cr.fetchall()]
|
||||
if (set(external_ids)-ids_set):
|
||||
# as installed modules have defined this element we must not delete it!
|
||||
continue
|
||||
|
||||
if name.startswith(EXT_ID_PREFIX_M2M_TABLE):
|
||||
name = name[len(EXT_ID_PREFIX_M2M_TABLE):]
|
||||
cr.execute("SELECT 1 FROM information_schema.tables WHERE table_name=%s", (name,))
|
||||
if cr.fetchone() and not name in to_drop_table:
|
||||
to_drop_table.append(name)
|
||||
continue
|
||||
|
||||
pair_to_unlink = (model, res_id)
|
||||
if pair_to_unlink not in to_unlink:
|
||||
to_unlink.append(pair_to_unlink)
|
||||
|
@ -890,11 +874,6 @@ class ir_model_data(osv.osv):
|
|||
except:
|
||||
_logger.info('Unable to force processing of workflow for item %s@%s in order to leave activity to be deleted', res_id, model)
|
||||
|
||||
# drop m2m relation tables
|
||||
for table in to_drop_table:
|
||||
cr.execute('DROP TABLE %s CASCADE'% (table),)
|
||||
_logger.info('Dropped table %s', table)
|
||||
|
||||
def unlink_if_refcount(to_unlink):
|
||||
for model, res_id in to_unlink:
|
||||
external_ids = self.search(cr, uid, [('model', '=', model),('res_id', '=', res_id)])
|
||||
|
|
|
@ -64,7 +64,6 @@ class ir_model_constraint(Model):
|
|||
cr.execute("""SELECT 1 from pg_constraint cs JOIN pg_class cl ON (cs.conrelid = cl.oid)
|
||||
WHERE cs.contype=%s and cs.conname=%s and cl.relname=%s""", ('f', name, model_obj._table))
|
||||
if cr.fetchone():
|
||||
print '>>> ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model_obj._table, name)
|
||||
cr.execute('ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model_obj._table, name),)
|
||||
_logger.info('Dropped FK CONSTRAINT %s@%s', name, model)
|
||||
|
||||
|
@ -73,7 +72,6 @@ class ir_model_constraint(Model):
|
|||
cr.execute("""SELECT 1 from pg_constraint cs JOIN pg_class cl ON (cs.conrelid = cl.oid)
|
||||
WHERE cs.contype=%s and cs.conname=%s and cl.relname=%s""", ('u', name, model_obj._table))
|
||||
if cr.fetchone():
|
||||
print '>>> ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model_obj._table, name)
|
||||
cr.execute('ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model_obj._table, name),)
|
||||
_logger.info('Dropped CONSTRAINT %s@%s', name, model)
|
||||
|
||||
|
|
|
@ -365,9 +365,12 @@ class module(osv.osv):
|
|||
tables, columns, constraints, etc."""
|
||||
ir_model_data = self.pool.get('ir.model.data')
|
||||
ir_model_constraint = self.pool.get('ir.model.constraint')
|
||||
ir_model_relation = self.pool.get('ir.model.relation')
|
||||
modules_to_remove = [m.name for m in self.browse(cr, uid, ids, context)]
|
||||
constraint_ids = ir_model_constraint.search(cr, uid, [('module', 'in', modules_to_remove)])
|
||||
ir_model_constraint._module_data_uninstall(cr, uid, constraint_ids, context)
|
||||
relation_ids = ir_model_relation.search(cr, uid, [('module', 'in', modules_to_remove)])
|
||||
ir_model_relation._module_data_uninstall(cr, uid, relation_ids, context)
|
||||
data_ids = ir_model_data.search(cr, uid, [('module', 'in', modules_to_remove)])
|
||||
ir_model_data._module_data_uninstall(cr, uid, data_ids, context)
|
||||
ir_model_data.unlink(cr, uid, data_ids, context)
|
||||
|
|
|
@ -70,9 +70,6 @@ _schema = logging.getLogger(__name__ + '.schema')
|
|||
# List of etree._Element subclasses that we choose to ignore when parsing XML.
|
||||
from openerp.tools import SKIPPED_ELEMENT_TYPES
|
||||
|
||||
# Prefixes for external IDs of schema elements
|
||||
EXT_ID_PREFIX_M2M_TABLE = "_m2m_rel_table_"
|
||||
|
||||
regex_order = re.compile('^(([a-z0-9_]+|"[a-z0-9_]+")( *desc| *asc)?( *, *|))+$', re.I)
|
||||
regex_object_name = re.compile(r'^[a-z0-9_.]+$')
|
||||
|
||||
|
@ -2743,6 +2740,7 @@ class BaseModel(object):
|
|||
self._table, column['attname'])
|
||||
|
||||
def _save_constraint(self, cr, constraint_name, type):
|
||||
""" TODO """
|
||||
assert type in ('f', 'u')
|
||||
cr.execute("""
|
||||
SELECT 1 FROM ir_model_constraint, ir_module_module
|
||||
|
@ -2759,13 +2757,20 @@ class BaseModel(object):
|
|||
(SELECT id FROM ir_model WHERE model=%s), %s)""",
|
||||
(constraint_name, self._module, self._name, type))
|
||||
|
||||
# quick creation of ir.model.data entry to make uninstall of schema elements easier
|
||||
def _make_ext_id(self, cr, ext_id):
|
||||
cr.execute('SELECT 1 FROM ir_model_data WHERE name=%s AND module=%s', (ext_id, self._module))
|
||||
def _save_relation_table(self, cr, relation_table):
|
||||
""" TODO """
|
||||
cr.execute("""
|
||||
SELECT 1 FROM ir_model_relation, ir_module_module
|
||||
WHERE ir_model_relation.module=ir_module_module.id
|
||||
AND ir_model_relation.name=%s
|
||||
AND ir_module_module.name=%s
|
||||
""", (relation_table, self._module))
|
||||
if not cr.rowcount:
|
||||
cr.execute("""INSERT INTO ir_model_data (name,date_init,date_update,module,model)
|
||||
VALUES (%s, now() AT TIME ZONE 'UTC', now() AT TIME ZONE 'UTC', %s, %s)""",
|
||||
(ext_id, self._module, self._name))
|
||||
cr.execute("""INSERT INTO ir_model_relation (name, date_init, date_update, module, model)
|
||||
VALUES (%s, now() AT TIME ZONE 'UTC', now() AT TIME ZONE 'UTC',
|
||||
(SELECT id FROM ir_module_module WHERE name=%s),
|
||||
(SELECT id FROM ir_model WHERE model=%s))""",
|
||||
(relation_table, self._module, self._name))
|
||||
|
||||
# checked version: for direct m2o starting from `self`
|
||||
def _m2o_add_foreign_key_checked(self, source_field, dest_model, ondelete):
|
||||
|
@ -3195,7 +3200,7 @@ class BaseModel(object):
|
|||
|
||||
def _m2m_raise_or_create_relation(self, cr, f):
|
||||
m2m_tbl, col1, col2 = f._sql_names(self)
|
||||
self._make_ext_id(cr, EXT_ID_PREFIX_M2M_TABLE + m2m_tbl)
|
||||
self._save_relation_table(cr, m2m_tbl)
|
||||
cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (m2m_tbl,))
|
||||
if not cr.dictfetchall():
|
||||
if not self.pool.get(f._obj):
|
||||
|
|
|
@ -9,6 +9,7 @@ class test_uninstall_model(Model):
|
|||
_columns = {
|
||||
'name': fields.char('Name', size=64),
|
||||
'ref': fields.many2one('res.users', string='User'),
|
||||
'rel': fields.many2many('res.users', string='Users'),
|
||||
}
|
||||
|
||||
_sql_constraints = [
|
||||
|
|
Loading…
Reference in New Issue