Browse Source

Merge pull request #95 from acsone/8.0-add-database_cleanup

[ADD] module database_cleanup
pull/97/head
Holger Brunn 10 years ago
parent
commit
6dc8a901ae
  1. 15
      database_cleanup/README.rst
  2. 1
      database_cleanup/__init__.py
  3. 37
      database_cleanup/__openerp__.py
  4. 6
      database_cleanup/model/__init__.py
  5. 154
      database_cleanup/model/purge_columns.py
  6. 106
      database_cleanup/model/purge_data.py
  7. 135
      database_cleanup/model/purge_models.py
  8. 91
      database_cleanup/model/purge_modules.py
  9. 138
      database_cleanup/model/purge_tables.py
  10. 64
      database_cleanup/model/purge_wizard.py
  11. BIN
      database_cleanup/static/src/img/icon.png
  12. 48
      database_cleanup/view/menu.xml
  13. 37
      database_cleanup/view/purge_columns.xml
  14. 37
      database_cleanup/view/purge_data.xml
  15. 36
      database_cleanup/view/purge_models.xml
  16. 36
      database_cleanup/view/purge_modules.xml
  17. 36
      database_cleanup/view/purge_tables.xml

15
database_cleanup/README.rst

@ -0,0 +1,15 @@
Clean your OpenERP database from remnants of modules, models, columns and
tables left by uninstalled modules (prior to 7.0) or a homebrew database
upgrade to a new major version of OpenERP.
After installation of this module, go to the Settings menu -> Technical ->
Database cleanup. Go through the modules, models, columns and tables
entries under this menu (in that order) and find out if there is orphaned data
in your database. You can either delete entries by line, or sweep all entries
in one big step (if you are *really* confident).
Caution! This module is potentially harmful and can *easily* destroy the
integrity of your data. Do not use if you are not entirely comfortable
with the technical details of the OpenERP data model of *all* the modules
that have ever been installed on your database, and do not purge any module,
model, column or table if you do not know exactly what you are doing.

1
database_cleanup/__init__.py

@ -0,0 +1 @@
from . import model

37
database_cleanup/__openerp__.py

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Database cleanup',
'version': '0.1',
'author': 'Therp BV',
'depends': ['base'],
'license': 'AGPL-3',
'category': 'Tools',
'data': [
'view/purge_modules.xml',
'view/purge_models.xml',
'view/purge_columns.xml',
'view/purge_tables.xml',
'view/purge_data.xml',
'view/menu.xml',
],
}

6
database_cleanup/model/__init__.py

@ -0,0 +1,6 @@
from . import purge_wizard
from . import purge_modules
from . import purge_models
from . import purge_columns
from . import purge_tables
from . import purge_data

154
database_cleanup/model/purge_columns.py

@ -0,0 +1,154 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
from openerp.tools.translate import _
class CleanupPurgeLineColumn(orm.TransientModel):
_inherit = 'cleanup.purge.line'
_name = 'cleanup.purge.line.column'
_columns = {
'model_id': fields.many2one(
'ir.model', 'Model',
required=True, ondelete='CASCADE'),
'wizard_id': fields.many2one(
'cleanup.purge.wizard.column', 'Purge Wizard', readonly=True),
}
def purge(self, cr, uid, ids, context=None):
"""
Unlink columns upon manual confirmation.
"""
for line in self.browse(cr, uid, ids, context=context):
if line.purged:
continue
model_pool = self.pool[line.model_id.model]
# Check whether the column actually still exists.
# Inheritance such as stock.picking.in from stock.picking
# can lead to double attempts at removal
cr.execute(
'SELECT count(attname) FROM pg_attribute '
'WHERE attrelid = '
'( SELECT oid FROM pg_class WHERE relname = %s ) '
'AND attname = %s',
(model_pool._table, line.name))
if not cr.fetchone()[0]:
continue
self.logger.info(
'Dropping column %s from table %s',
line.name, model_pool._table)
cr.execute(
"""
ALTER TABLE "%s" DROP COLUMN "%s"
""" % (model_pool._table, line.name))
line.write({'purged': True})
cr.commit()
return True
class CleanupPurgeWizardColumn(orm.TransientModel):
_inherit = 'cleanup.purge.wizard'
_name = 'cleanup.purge.wizard.column'
# List of known columns in use without corresponding fields
# Format: {table: [fields]}
blacklist = {
'wkf_instance': ['uid'], # lp:1277899
}
def default_get(self, cr, uid, fields, context=None):
res = super(CleanupPurgeWizardColumn, self).default_get(
cr, uid, fields, context=context)
if 'name' in fields:
res['name'] = _('Purge columns')
return res
def get_orphaned_columns(self, cr, uid, model_pools, context=None):
"""
From openobject-server/openerp/osv/orm.py
Iterate on the database columns to identify columns
of fields which have been removed
"""
columns = list(set([
column for model_pool in model_pools
for column in model_pool._columns
if not (isinstance(model_pool._columns[column], fields.function)
and not model_pool._columns[column].store)
]))
columns += orm.MAGIC_COLUMNS
columns += self.blacklist.get(model_pools[0]._table, [])
cr.execute("SELECT a.attname"
" FROM pg_class c, pg_attribute a"
" WHERE c.relname=%s"
" AND c.oid=a.attrelid"
" AND a.attisdropped=%s"
" AND pg_catalog.format_type(a.atttypid, a.atttypmod)"
" NOT IN ('cid', 'tid', 'oid', 'xid')"
" AND a.attname NOT IN %s",
(model_pools[0]._table, False, tuple(columns))),
return [column[0] for column in cr.fetchall()]
def find(self, cr, uid, context=None):
"""
Search for columns that are not in the corresponding model.
Group models by table to prevent false positives for columns
that are only in some of the models sharing the same table.
Example of this is 'sale_id' not being a field of stock.picking.in
"""
res = []
model_pool = self.pool['ir.model']
model_ids = model_pool.search(cr, uid, [], context=context)
# mapping of tables to tuples (model id, [pool1, pool2, ...])
table2model = {}
for model in model_pool.browse(cr, uid, model_ids, context=context):
model_pool = self.pool.get(model.model)
if not model_pool or not model_pool._auto:
continue
table2model.setdefault(
model_pool._table, (model.id, []))[1].append(model_pool)
for table, model_spec in table2model.iteritems():
for column in self.get_orphaned_columns(
cr, uid, model_spec[1], context=context):
res.append((0, 0, {
'name': column,
'model_id': model_spec[0]}))
if not res:
raise orm.except_orm(
_('Nothing to do'),
_('No orphaned columns found'))
return res
_columns = {
'purge_line_ids': fields.one2many(
'cleanup.purge.line.column',
'wizard_id', 'Columns to purge'),
}

106
database_cleanup/model/purge_data.py

@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
from openerp.tools.translate import _
class CleanupPurgeLineData(orm.TransientModel):
_inherit = 'cleanup.purge.line'
_name = 'cleanup.purge.line.data'
_columns = {
'data_id': fields.many2one(
'ir.model.data', 'Data entry',
ondelete='SET NULL'),
'wizard_id': fields.many2one(
'cleanup.purge.wizard.data', 'Purge Wizard', readonly=True),
}
def purge(self, cr, uid, ids, context=None):
"""
Unlink data entries upon manual confirmation.
"""
data_ids = []
for line in self.browse(cr, uid, ids, context=context):
if line.purged or not line.data_id:
continue
data_ids.append(line.data_id.id)
self.logger.info('Purging data entry: %s', line.name)
self.pool['ir.model.data'].unlink(cr, uid, data_ids, context=context)
return self.write(cr, uid, ids, {'purged': True}, context=context)
class CleanupPurgeWizardData(orm.TransientModel):
_inherit = 'cleanup.purge.wizard'
_name = 'cleanup.purge.wizard.data'
def default_get(self, cr, uid, fields, context=None):
res = super(CleanupPurgeWizardData, self).default_get(
cr, uid, fields, context=context)
if 'name' in fields:
res['name'] = _('Purge data')
return res
def find(self, cr, uid, context=None):
"""
Collect all rows from ir_model_data that refer
to a nonexisting model, or to a nonexisting
row in the model's table.
"""
res = []
data_pool = self.pool['ir.model.data']
data_ids = []
unknown_models = []
cr.execute("""SELECT DISTINCT(model) FROM ir_model_data""")
for (model,) in cr.fetchall():
if not model:
continue
if not self.pool.get(model):
unknown_models.append(model)
continue
cr.execute(
"""
SELECT id FROM ir_model_data
WHERE model = %%s
AND res_id IS NOT NULL
AND res_id NOT IN (
SELECT id FROM %s)
""" % self.pool[model]._table, (model,))
data_ids += [data_row[0] for data_row in cr.fetchall()]
data_ids += data_pool.search(
cr, uid, [('model', 'in', unknown_models)], context=context)
for data in data_pool.browse(cr, uid, data_ids, context=context):
res.append((0, 0, {
'data_id': data.id,
'name': "%s.%s, object of type %s" % (
data.module, data.name, data.model)}))
if not res:
raise orm.except_orm(
_('Nothing to do'),
_('No orphaned data entries found'))
return res
_columns = {
'purge_line_ids': fields.one2many(
'cleanup.purge.line.data',
'wizard_id', 'Data to purge'),
}

135
database_cleanup/model/purge_models.py

@ -0,0 +1,135 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
from openerp.tools.translate import _
from openerp.addons.base.ir.ir_model import MODULE_UNINSTALL_FLAG
class IrModel(orm.Model):
_inherit = 'ir.model'
def _drop_table(self, cr, uid, ids, context=None):
# Allow to skip this step during model unlink
# The super method crashes if the model cannot be instantiated
if context and context.get('no_drop_table'):
return True
return super(IrModel, self)._drop_table(cr, uid, ids, context=context)
class CleanupPurgeLineModel(orm.TransientModel):
_inherit = 'cleanup.purge.line'
_name = 'cleanup.purge.line.model'
_columns = {
'wizard_id': fields.many2one(
'cleanup.purge.wizard.model', 'Purge Wizard', readonly=True),
}
def purge(self, cr, uid, ids, context=None):
"""
Unlink models upon manual confirmation.
"""
model_pool = self.pool['ir.model']
attachment_pool = self.pool['ir.attachment']
constraint_pool = self.pool['ir.model.constraint']
fields_pool = self.pool['ir.model.fields']
relation_pool = self.pool['ir.model.relation']
local_context = (context or {}).copy()
local_context.update({
MODULE_UNINSTALL_FLAG: True,
'no_drop_table': True,
})
for line in self.browse(cr, uid, ids, context=context):
cr.execute(
"SELECT id, model from ir_model WHERE model = %s",
(line.name,))
row = cr.fetchone()
if row:
self.logger.info('Purging model %s', row[1])
attachment_ids = attachment_pool.search(
cr, uid, [('res_model', '=', line.name)], context=context)
if attachment_ids:
cr.execute(
"UPDATE ir_attachment SET res_model = FALSE "
"WHERE id in %s",
(tuple(attachment_ids), ))
constraint_ids = constraint_pool.search(
cr, uid, [('model', '=', line.name)], context=context)
if constraint_ids:
constraint_pool.unlink(
cr, uid, constraint_ids, context=context)
relation_ids = fields_pool.search(
cr, uid, [('relation', '=', row[1])], context=context)
for relation in relation_ids:
try:
# Fails if the model on the target side
# cannot be instantiated
fields_pool.unlink(cr, uid, [relation],
context=local_context)
except KeyError:
pass
except AttributeError:
pass
relation_ids = relation_pool.search(
cr, uid, [('model', '=', line.name)], context=context)
for relation in relation_ids:
relation_pool.unlink(cr, uid, [relation],
context=local_context)
model_pool.unlink(cr, uid, [row[0]], context=local_context)
line.write({'purged': True})
cr.commit()
return True
class CleanupPurgeWizardModel(orm.TransientModel):
_inherit = 'cleanup.purge.wizard'
_name = 'cleanup.purge.wizard.model'
def default_get(self, cr, uid, fields, context=None):
res = super(CleanupPurgeWizardModel, self).default_get(
cr, uid, fields, context=context)
if 'name' in fields:
res['name'] = _('Purge models')
return res
def find(self, cr, uid, context=None):
"""
Search for models that cannot be instantiated.
"""
res = []
cr.execute("SELECT model from ir_model")
for (model,) in cr.fetchall():
if not self.pool.get(model):
res.append((0, 0, {'name': model}))
if not res:
raise orm.except_orm(
_('Nothing to do'),
_('No orphaned models found'))
return res
_columns = {
'purge_line_ids': fields.one2many(
'cleanup.purge.line.model',
'wizard_id', 'Models to purge'),
}

91
database_cleanup/model/purge_modules.py

@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import pooler
from openerp.osv import orm, fields
from openerp.modules.module import get_module_path
from openerp.tools.translate import _
class CleanupPurgeLineModule(orm.TransientModel):
_inherit = 'cleanup.purge.line'
_name = 'cleanup.purge.line.module'
_columns = {
'wizard_id': fields.many2one(
'cleanup.purge.wizard.module', 'Purge Wizard', readonly=True),
}
def purge(self, cr, uid, ids, context=None):
"""
Uninstall modules upon manual confirmation, then reload
the database.
"""
module_pool = self.pool['ir.module.module']
lines = self.browse(cr, uid, ids, context=context)
module_names = [line.name for line in lines if not line.purged]
module_ids = module_pool.search(
cr, uid, [('name', 'in', module_names)], context=context)
if not module_ids:
return True
self.logger.info('Purging modules %s', ', '.join(module_names))
module_pool.write(
cr, uid, module_ids, {'state': 'to remove'}, context=context)
cr.commit()
_db, _pool = pooler.restart_pool(cr.dbname, update_module=True)
module_pool.unlink(cr, uid, module_ids, context=context)
return self.write(cr, uid, ids, {'purged': True}, context=context)
class CleanupPurgeWizardModule(orm.TransientModel):
_inherit = 'cleanup.purge.wizard'
_name = 'cleanup.purge.wizard.module'
def default_get(self, cr, uid, fields, context=None):
res = super(CleanupPurgeWizardModule, self).default_get(
cr, uid, fields, context=context)
if 'name' in fields:
res['name'] = _('Purge modules')
return res
def find(self, cr, uid, context=None):
module_pool = self.pool['ir.module.module']
module_ids = module_pool.search(cr, uid, [], context=context)
res = []
for module in module_pool.browse(cr, uid, module_ids, context=context):
if get_module_path(module.name):
continue
if module.state == 'uninstalled':
module_pool.unlink(cr, uid, module.id, context=context)
continue
res.append((0, 0, {'name': module.name}))
if not res:
raise orm.except_orm(
_('Nothing to do'),
_('No modules found to purge'))
return res
_columns = {
'purge_line_ids': fields.one2many(
'cleanup.purge.line.module',
'wizard_id', 'Modules to purge'),
}

138
database_cleanup/model/purge_tables.py

@ -0,0 +1,138 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import orm, fields
from openerp.tools.translate import _
class CleanupPurgeLineTable(orm.TransientModel):
_inherit = 'cleanup.purge.line'
_name = 'cleanup.purge.line.table'
_columns = {
'wizard_id': fields.many2one(
'cleanup.purge.wizard.table', 'Purge Wizard', readonly=True),
}
def purge(self, cr, uid, ids, context=None):
"""
Unlink tables upon manual confirmation.
"""
lines = self.browse(cr, uid, ids, context=context)
tables = [line.name for line in lines]
for line in lines:
if line.purged:
continue
# Retrieve constraints on the tables to be dropped
# This query is referenced in numerous places
# on the Internet but credits probably go to Tom Lane
# in this post http://www.postgresql.org/\
# message-id/22895.1226088573@sss.pgh.pa.us
# Only using the constraint name and the source table,
# but I'm leaving the rest in for easier debugging
cr.execute(
"""
SELECT conname, confrelid::regclass, af.attname AS fcol,
conrelid::regclass, a.attname AS col
FROM pg_attribute af, pg_attribute a,
(SELECT conname, conrelid, confrelid,conkey[i] AS conkey,
confkey[i] AS confkey
FROM (select conname, conrelid, confrelid, conkey,
confkey, generate_series(1,array_upper(conkey,1)) AS i
FROM pg_constraint WHERE contype = 'f') ss) ss2
WHERE af.attnum = confkey AND af.attrelid = confrelid AND
a.attnum = conkey AND a.attrelid = conrelid
AND confrelid::regclass = '%s'::regclass;
""" % line.name)
for constraint in cr.fetchall():
if constraint[3] in tables:
self.logger.info(
'Dropping constraint %s on table %s (to be dropped)',
constraint[0], constraint[3])
cr.execute(
"ALTER TABLE %s DROP CONSTRAINT %s" % (
constraint[3], constraint[0]))
self.logger.info(
'Dropping table %s', line.name)
cr.execute("DROP TABLE \"%s\"" % (line.name,))
line.write({'purged': True})
cr.commit()
return True
class CleanupPurgeWizardTable(orm.TransientModel):
_inherit = 'cleanup.purge.wizard'
_name = 'cleanup.purge.wizard.table'
def default_get(self, cr, uid, fields, context=None):
res = super(CleanupPurgeWizardTable, self).default_get(
cr, uid, fields, context=context)
if 'name' in fields:
res['name'] = _('Purge tables')
return res
def find(self, cr, uid, context=None):
"""
Search for tables that cannot be instantiated.
Ignore views for now.
"""
model_ids = self.pool['ir.model'].search(cr, uid, [], context=context)
# Start out with known tables with no model
known_tables = ['wkf_witm_trans']
for model in self.pool['ir.model'].browse(
cr, uid, model_ids, context=context):
model_pool = self.pool.get(model.model)
if not model_pool:
continue
known_tables.append(model_pool._table)
known_tables += [
column._sql_names(model_pool)[0]
for column in model_pool._columns.values()
if column._type == 'many2many'
# unstored function fields of type m2m don't have _rel
and hasattr(column, '_rel')
]
# Cannot pass table names as a psycopg argument
known_tables_repr = ",".join(
[("'%s'" % table) for table in known_tables])
cr.execute(
"""
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
AND table_name NOT IN (%s)""" % known_tables_repr)
res = [(0, 0, {'name': row[0]}) for row in cr.fetchall()]
if not res:
raise orm.except_orm(
_('Nothing to do'),
_('No orphaned tables found'))
return res
_columns = {
'purge_line_ids': fields.one2many(
'cleanup.purge.line.table',
'wizard_id', 'Tables to purge'),
}

64
database_cleanup/model/purge_wizard.py

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import logging
from openerp.osv import orm, fields
class CleanupPurgeLine(orm.AbstractModel):
""" Abstract base class for the purge wizard lines """
_name = 'cleanup.purge.line'
_columns = {
'name': fields.char('Name', size=256, readonly=True),
'purged': fields.boolean('Purged', readonly=True),
}
logger = logging.getLogger('openerp.addons.database_cleanup')
def purge(self, cr, uid, ids, context=None):
raise NotImplementedError
class PurgeWizard(orm.AbstractModel):
""" Abstract base class for the purge wizards """
_name = 'cleanup.purge.wizard'
def default_get(self, cr, uid, fields, context=None):
res = super(PurgeWizard, self).default_get(
cr, uid, fields, context=context)
if 'purge_line_ids' in fields:
res['purge_line_ids'] = self.find(cr, uid, context=None)
return res
def find(self, cr, uid, ids, context=None):
raise NotImplementedError
def purge_all(self, cr, uid, ids, context=None):
line_pool = self.pool[self._columns['purge_line_ids']._obj]
for wizard in self.browse(cr, uid, ids, context=context):
line_pool.purge(
cr, uid, [line.id for line in wizard.purge_line_ids],
context=context)
return True
_columns = {
'name': fields.char('Name', size=64, readonly=True),
}

BIN
database_cleanup/static/src/img/icon.png

After

Width: 200  |  Height: 200  |  Size: 30 KiB

48
database_cleanup/view/menu.xml

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record model="ir.ui.menu" id="menu_database_cleanup">
<field name="name">Database cleanup</field>
<field name="sequence" eval="10" />
<!-- attach to Settings -> Technical -->
<field name="parent_id" ref="base.menu_custom"/>
</record>
<record model="ir.ui.menu" id="menu_purge_modules">
<field name="name">Purge obsolete modules</field>
<field name="sequence" eval="10" />
<field name="action" ref="action_purge_modules" />
<field name="parent_id" ref="menu_database_cleanup"/>
</record>
<record model="ir.ui.menu" id="menu_purge_models">
<field name="name">Purge obsolete models</field>
<field name="sequence" eval="20" />
<field name="action" ref="action_purge_models" />
<field name="parent_id" ref="menu_database_cleanup"/>
</record>
<record model="ir.ui.menu" id="menu_purge_columns">
<field name="name">Purge obsolete columns</field>
<field name="sequence" eval="30" />
<field name="action" ref="action_purge_columns" />
<field name="parent_id" ref="menu_database_cleanup"/>
</record>
<record model="ir.ui.menu" id="menu_purge_tables">
<field name="name">Purge obsolete tables</field>
<field name="sequence" eval="40" />
<field name="action" ref="action_purge_tables" />
<field name="parent_id" ref="menu_database_cleanup"/>
</record>
<record model="ir.ui.menu" id="menu_purge_data">
<field name="name">Purge obsolete data entries</field>
<field name="sequence" eval="50" />
<field name="action" ref="action_purge_data" />
<field name="parent_id" ref="menu_database_cleanup"/>
</record>
</data>
</openerp>

37
database_cleanup/view/purge_columns.xml

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="purge_columns_view" model="ir.ui.view">
<field name="name">Form view for purge columns wizard</field>
<field name="model">cleanup.purge.wizard.column</field>
<field name="arch" type="xml">
<form string="Purge columns" version="7.0">
<h1>
<field name="name"/>
</h1>
<button type="object" name="purge_all" string="Purge all columns" />
<field name="purge_line_ids" colspan="4" nolabel="1">
<tree string="Purge columns">
<field name="name" />
<field name="model_id" />
<field name="purged" invisible="0" />
<button type="object" name="purge"
icon="gtk-cancel" string="Purge this column"
attrs="{'invisible': [('purged', '=', True)]}"/>
</tree>
</field>
</form>
</field>
</record>
<record id="action_purge_columns" model="ir.actions.act_window">
<field name="name">Purge columns</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">cleanup.purge.wizard.column</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
</record>
</data>
</openerp>

37
database_cleanup/view/purge_data.xml

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="purge_data_view" model="ir.ui.view">
<field name="name">Form view for purge data wizard</field>
<field name="model">cleanup.purge.wizard.data</field>
<field name="arch" type="xml">
<form string="Purge data entries that refer to missing resources" version="7.0">
<h1>
<field name="name"/>
</h1>
<button type="object" name="purge_all" string="Purge all data" />
<field name="purge_line_ids" colspan="4" nolabel="1">
<tree string="Purge data">
<field name="name" />
<field name="data_id" />
<field name="purged" invisible="0" />
<button type="object" name="purge"
icon="gtk-cancel" string="Purge this data"
attrs="{'invisible': [('purged', '=', True)]}"/>
</tree>
</field>
</form>
</field>
</record>
<record id="action_purge_data" model="ir.actions.act_window">
<field name="name">Purge data entries that refer to missing resources</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">cleanup.purge.wizard.data</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
</record>
</data>
</openerp>

36
database_cleanup/view/purge_models.xml

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="purge_models_view" model="ir.ui.view">
<field name="name">Form view for purge models wizard</field>
<field name="model">cleanup.purge.wizard.model</field>
<field name="arch" type="xml">
<form string="Purge models" version="7.0">
<h1>
<field name="name"/>
</h1>
<button type="object" name="purge_all" string="Purge all models" />
<field name="purge_line_ids" colspan="4" nolabel="1">
<tree string="Purge models">
<field name="name" />
<field name="purged" invisible="0" />
<button type="object" name="purge"
icon="gtk-cancel" string="Purge this model"
attrs="{'invisible': [('purged', '=', True)]}"/>
</tree>
</field>
</form>
</field>
</record>
<record id="action_purge_models" model="ir.actions.act_window">
<field name="name">Purge models</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">cleanup.purge.wizard.model</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
</record>
</data>
</openerp>

36
database_cleanup/view/purge_modules.xml

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="purge_modules_view" model="ir.ui.view">
<field name="name">Form view for purge modules wizard</field>
<field name="model">cleanup.purge.wizard.module</field>
<field name="arch" type="xml">
<form string="Purge modules" version="7.0">
<h1>
<field name="name"/>
</h1>
<button type="object" name="purge_all" string="Purge all modules" />
<field name="purge_line_ids" colspan="4" nolabel="1">
<tree string="Purge modules">
<field name="name" />
<field name="purged" invisible="0" />
<button type="object" name="purge"
icon="gtk-cancel" string="Purge this module"
attrs="{'invisible': [('purged', '=', True)]}"/>
</tree>
</field>
</form>
</field>
</record>
<record id="action_purge_modules" model="ir.actions.act_window">
<field name="name">Purge modules</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">cleanup.purge.wizard.module</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
</record>
</data>
</openerp>

36
database_cleanup/view/purge_tables.xml

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="purge_tables_view" model="ir.ui.view">
<field name="name">Form view for purge tables wizard</field>
<field name="model">cleanup.purge.wizard.table</field>
<field name="arch" type="xml">
<form string="Purge tables" version="7.0">
<h1>
<field name="name"/>
</h1>
<button type="object" name="purge_all" string="Purge all tables" />
<field name="purge_line_ids" colspan="4" nolabel="1">
<tree string="Purge tables">
<field name="name" />
<field name="purged" invisible="0" />
<button type="object" name="purge"
icon="gtk-cancel" string="Purge this table"
attrs="{'invisible': [('purged', '=', True)]}"/>
</tree>
</field>
</form>
</field>
</record>
<record id="action_purge_tables" model="ir.actions.act_window">
<field name="name">Purge tables</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">cleanup.purge.wizard.table</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
</record>
</data>
</openerp>
Loading…
Cancel
Save