[IMP] database_cleanup: black, isort, prettier
parent
5d0be1e42e
commit
d1acfa4b4a
|
@ -1,23 +1,23 @@
|
|||
# Copyright 2014-2016 Therp BV <http://therp.nl>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
{
|
||||
'name': 'Database cleanup',
|
||||
'version': '12.0.1.0.1',
|
||||
'author': "Therp BV,Odoo Community Association (OCA)",
|
||||
'depends': ['base'],
|
||||
'license': 'AGPL-3',
|
||||
'category': 'Tools',
|
||||
'data': [
|
||||
"name": "Database cleanup",
|
||||
"version": "12.0.1.0.1",
|
||||
"author": "Therp BV,Odoo Community Association (OCA)",
|
||||
"depends": ["base"],
|
||||
"license": "AGPL-3",
|
||||
"category": "Tools",
|
||||
"data": [
|
||||
"views/purge_wizard.xml",
|
||||
'views/purge_menus.xml',
|
||||
'views/purge_modules.xml',
|
||||
'views/purge_models.xml',
|
||||
'views/purge_columns.xml',
|
||||
'views/purge_tables.xml',
|
||||
'views/purge_data.xml',
|
||||
"views/purge_menus.xml",
|
||||
"views/purge_modules.xml",
|
||||
"views/purge_models.xml",
|
||||
"views/purge_columns.xml",
|
||||
"views/purge_tables.xml",
|
||||
"views/purge_data.xml",
|
||||
"views/create_indexes.xml",
|
||||
'views/purge_properties.xml',
|
||||
'views/menu.xml',
|
||||
"views/purge_properties.xml",
|
||||
"views/menu.xml",
|
||||
],
|
||||
'installable': True,
|
||||
"installable": True,
|
||||
}
|
||||
|
|
|
@ -14,11 +14,9 @@ class IdentifierAdapter(ISQLQuote):
|
|||
|
||||
def getquoted(self):
|
||||
def is_identifier_char(c):
|
||||
return c.isalnum() or c in ['_', '$']
|
||||
return c.isalnum() or c in ["_", "$"]
|
||||
|
||||
format_string = '"%s"'
|
||||
if not self.quote:
|
||||
format_string = '%s'
|
||||
return format_string % ''.join(
|
||||
filter(is_identifier_char, self.identifier)
|
||||
)
|
||||
format_string = "%s"
|
||||
return format_string % "".join(filter(is_identifier_char, self.identifier))
|
||||
|
|
|
@ -1,26 +1,27 @@
|
|||
# Copyright 2017 Therp BV <http://therp.nl>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
# pylint: disable=consider-merging-classes-inherited
|
||||
from ..identifier_adapter import IdentifierAdapter
|
||||
from odoo import api, fields, models
|
||||
|
||||
from ..identifier_adapter import IdentifierAdapter
|
||||
|
||||
|
||||
class CreateIndexesLine(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.line'
|
||||
_name = 'cleanup.create_indexes.line'
|
||||
_inherit = "cleanup.purge.line"
|
||||
_name = "cleanup.create_indexes.line"
|
||||
|
||||
purged = fields.Boolean('Created')
|
||||
wizard_id = fields.Many2one('cleanup.create_indexes.wizard')
|
||||
field_id = fields.Many2one('ir.model.fields', required=True)
|
||||
purged = fields.Boolean("Created")
|
||||
wizard_id = fields.Many2one("cleanup.create_indexes.wizard")
|
||||
field_id = fields.Many2one("ir.model.fields", required=True)
|
||||
|
||||
@api.multi
|
||||
def purge(self):
|
||||
tables = set()
|
||||
for field in self.mapped('field_id'):
|
||||
for field in self.mapped("field_id"):
|
||||
model = self.env[field.model]
|
||||
name = '%s_%s_index' % (model._table, field.name)
|
||||
name = "{}_{}_index".format(model._table, field.name)
|
||||
self.env.cr.execute(
|
||||
'create index %s ON %s (%s)',
|
||||
"create index %s ON %s (%s)",
|
||||
(
|
||||
IdentifierAdapter(name, quote=False),
|
||||
IdentifierAdapter(model._table),
|
||||
|
@ -29,54 +30,66 @@ class CreateIndexesLine(models.TransientModel):
|
|||
)
|
||||
tables.add(model._table)
|
||||
for table in tables:
|
||||
self.env.cr.execute(
|
||||
'analyze %s', (IdentifierAdapter(model._table),)
|
||||
)
|
||||
self.write({
|
||||
'purged': True,
|
||||
})
|
||||
self.env.cr.execute("analyze %s", (IdentifierAdapter(model._table),))
|
||||
self.write(
|
||||
{
|
||||
"purged": True,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class CreateIndexesWizard(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.wizard'
|
||||
_name = 'cleanup.create_indexes.wizard'
|
||||
_description = 'Create indexes'
|
||||
_inherit = "cleanup.purge.wizard"
|
||||
_name = "cleanup.create_indexes.wizard"
|
||||
_description = "Create indexes"
|
||||
|
||||
purge_line_ids = fields.One2many(
|
||||
'cleanup.create_indexes.line', 'wizard_id',
|
||||
"cleanup.create_indexes.line",
|
||||
"wizard_id",
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def find(self):
|
||||
res = list()
|
||||
for field in self.env['ir.model.fields'].search([
|
||||
('index', '=', True),
|
||||
]):
|
||||
for field in self.env["ir.model.fields"].search(
|
||||
[
|
||||
("index", "=", True),
|
||||
]
|
||||
):
|
||||
if field.model not in self.env.registry:
|
||||
continue
|
||||
model = self.env[field.model]
|
||||
name = '%s_%s_index' % (model._table, field.name)
|
||||
name = "{}_{}_index".format(model._table, field.name)
|
||||
self.env.cr.execute(
|
||||
'select indexname from pg_indexes '
|
||||
'where indexname=%s and tablename=%s',
|
||||
(name, model._table)
|
||||
"select indexname from pg_indexes "
|
||||
"where indexname=%s and tablename=%s",
|
||||
(name, model._table),
|
||||
)
|
||||
if self.env.cr.rowcount:
|
||||
continue
|
||||
|
||||
self.env.cr.execute(
|
||||
'select a.attname '
|
||||
'from pg_attribute a '
|
||||
'join pg_class c on a.attrelid=c.oid '
|
||||
'join pg_tables t on t.tablename=c.relname '
|
||||
'where attname=%s and c.relname=%s',
|
||||
(field.name, model._table,)
|
||||
"select a.attname "
|
||||
"from pg_attribute a "
|
||||
"join pg_class c on a.attrelid=c.oid "
|
||||
"join pg_tables t on t.tablename=c.relname "
|
||||
"where attname=%s and c.relname=%s",
|
||||
(
|
||||
field.name,
|
||||
model._table,
|
||||
),
|
||||
)
|
||||
if not self.env.cr.rowcount:
|
||||
continue
|
||||
|
||||
res.append((0, 0, {
|
||||
'name': '%s.%s' % (field.model, field.name),
|
||||
'field_id': field.id,
|
||||
}))
|
||||
res.append(
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"name": "{}.{}".format(field.model, field.name),
|
||||
"field_id": field.id,
|
||||
},
|
||||
)
|
||||
)
|
||||
return res
|
||||
|
|
|
@ -3,18 +3,19 @@
|
|||
# pylint: disable=consider-merging-classes-inherited
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
from ..identifier_adapter import IdentifierAdapter
|
||||
|
||||
|
||||
class CleanupPurgeLineColumn(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.line'
|
||||
_name = 'cleanup.purge.line.column'
|
||||
_description = 'Purge Column Wizard Lines'
|
||||
_inherit = "cleanup.purge.line"
|
||||
_name = "cleanup.purge.line.column"
|
||||
_description = "Purge Column Wizard Lines"
|
||||
|
||||
model_id = fields.Many2one('ir.model', 'Model', required=True,
|
||||
ondelete='CASCADE')
|
||||
model_id = fields.Many2one("ir.model", "Model", required=True, ondelete="CASCADE")
|
||||
wizard_id = fields.Many2one(
|
||||
'cleanup.purge.wizard.column', 'Purge Wizard', readonly=True)
|
||||
"cleanup.purge.wizard.column", "Purge Wizard", readonly=True
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def purge(self):
|
||||
|
@ -24,8 +25,9 @@ class CleanupPurgeLineColumn(models.TransientModel):
|
|||
if self:
|
||||
objs = self
|
||||
else:
|
||||
objs = self.env['cleanup.purge.line.column']\
|
||||
.browse(self._context.get('active_ids'))
|
||||
objs = self.env["cleanup.purge.line.column"].browse(
|
||||
self._context.get("active_ids")
|
||||
)
|
||||
for line in objs:
|
||||
if line.purged:
|
||||
continue
|
||||
|
@ -34,24 +36,23 @@ class CleanupPurgeLineColumn(models.TransientModel):
|
|||
# Inheritance such as stock.picking.in from stock.picking
|
||||
# can lead to double attempts at removal
|
||||
self.env.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))
|
||||
"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 self.env.cr.fetchone()[0]:
|
||||
continue
|
||||
|
||||
self.logger.info(
|
||||
'Dropping column %s from table %s',
|
||||
line.name, model_pool._table)
|
||||
"Dropping column %s from table %s", line.name, model_pool._table
|
||||
)
|
||||
self.env.cr.execute(
|
||||
'ALTER TABLE %s DROP COLUMN %s',
|
||||
(
|
||||
IdentifierAdapter(model_pool._table),
|
||||
IdentifierAdapter(line.name)
|
||||
))
|
||||
line.write({'purged': True})
|
||||
"ALTER TABLE %s DROP COLUMN %s",
|
||||
(IdentifierAdapter(model_pool._table), IdentifierAdapter(line.name)),
|
||||
)
|
||||
line.write({"purged": True})
|
||||
# we need this commit because the ORM will deadlock if
|
||||
# we still have a pending transaction
|
||||
self.env.cr.commit() # pylint: disable=invalid-commit
|
||||
|
@ -59,15 +60,15 @@ class CleanupPurgeLineColumn(models.TransientModel):
|
|||
|
||||
|
||||
class CleanupPurgeWizardColumn(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.wizard'
|
||||
_name = 'cleanup.purge.wizard.column'
|
||||
_description = 'Purge columns'
|
||||
_inherit = "cleanup.purge.wizard"
|
||||
_name = "cleanup.purge.wizard.column"
|
||||
_description = "Purge columns"
|
||||
|
||||
# List of known columns in use without corresponding fields
|
||||
# Format: {table: [fields]}
|
||||
blacklist = {
|
||||
'wkf_instance': ['uid'], # lp:1277899
|
||||
'res_users': ['password', 'password_crypt'],
|
||||
"wkf_instance": ["uid"], # lp:1277899
|
||||
"res_users": ["password", "password_crypt"],
|
||||
}
|
||||
|
||||
@api.model
|
||||
|
@ -77,12 +78,14 @@ class CleanupPurgeWizardColumn(models.TransientModel):
|
|||
Iterate on the database columns to identify columns
|
||||
of fields which have been removed
|
||||
"""
|
||||
columns = list(set([
|
||||
column.name
|
||||
for model_pool in model_pools
|
||||
for column in model_pool._fields.values()
|
||||
if not (column.compute is not None and not column.store)
|
||||
]))
|
||||
columns = list(
|
||||
{
|
||||
column.name
|
||||
for model_pool in model_pools
|
||||
for column in model_pool._fields.values()
|
||||
if not (column.compute is not None and not column.store)
|
||||
}
|
||||
)
|
||||
columns += models.MAGIC_COLUMNS
|
||||
columns += self.blacklist.get(model_pools[0]._table, [])
|
||||
|
||||
|
@ -92,7 +95,8 @@ class CleanupPurgeWizardColumn(models.TransientModel):
|
|||
"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, tuple(columns)))
|
||||
(model_pools[0]._table, tuple(columns)),
|
||||
)
|
||||
return [column for column, in self.env.cr.fetchall()]
|
||||
|
||||
@api.model
|
||||
|
@ -109,24 +113,23 @@ class CleanupPurgeWizardColumn(models.TransientModel):
|
|||
# mapping of tables to tuples (model id, [pool1, pool2, ...])
|
||||
table2model = {}
|
||||
|
||||
for model in self.env['ir.model'].search([]):
|
||||
for model in self.env["ir.model"].search([]):
|
||||
if model.model not in self.env:
|
||||
continue
|
||||
model_pool = self.env[model.model]
|
||||
if not model_pool._auto:
|
||||
continue
|
||||
table2model.setdefault(
|
||||
model_pool._table, (model.id, [])
|
||||
)[1].append(model_pool)
|
||||
table2model.setdefault(model_pool._table, (model.id, []))[1].append(
|
||||
model_pool
|
||||
)
|
||||
|
||||
for table, model_spec in table2model.items():
|
||||
for column in self.get_orphaned_columns(model_spec[1]):
|
||||
res.append((0, 0, {
|
||||
'name': column,
|
||||
'model_id': model_spec[0]}))
|
||||
res.append((0, 0, {"name": column, "model_id": model_spec[0]}))
|
||||
if not res:
|
||||
raise UserError(_('No orphaned columns found'))
|
||||
raise UserError(_("No orphaned columns found"))
|
||||
return res
|
||||
|
||||
purge_line_ids = fields.One2many(
|
||||
'cleanup.purge.line.column', 'wizard_id', 'Columns to purge')
|
||||
"cleanup.purge.line.column", "wizard_id", "Columns to purge"
|
||||
)
|
||||
|
|
|
@ -2,16 +2,18 @@
|
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
from ..identifier_adapter import IdentifierAdapter
|
||||
|
||||
|
||||
class CleanupPurgeLineData(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.line'
|
||||
_name = 'cleanup.purge.line.data'
|
||||
_inherit = "cleanup.purge.line"
|
||||
_name = "cleanup.purge.line.data"
|
||||
|
||||
data_id = fields.Many2one('ir.model.data', 'Data entry')
|
||||
data_id = fields.Many2one("ir.model.data", "Data entry")
|
||||
wizard_id = fields.Many2one(
|
||||
'cleanup.purge.wizard.data', 'Purge Wizard', readonly=True)
|
||||
"cleanup.purge.wizard.data", "Purge Wizard", readonly=True
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def purge(self):
|
||||
|
@ -19,18 +21,19 @@ class CleanupPurgeLineData(models.TransientModel):
|
|||
if self:
|
||||
objs = self
|
||||
else:
|
||||
objs = self.env['cleanup.purge.line.data']\
|
||||
.browse(self._context.get('active_ids'))
|
||||
objs = self.env["cleanup.purge.line.data"].browse(
|
||||
self._context.get("active_ids")
|
||||
)
|
||||
to_unlink = objs.filtered(lambda x: not x.purged and x.data_id)
|
||||
self.logger.info('Purging data entries: %s', to_unlink.mapped('name'))
|
||||
to_unlink.mapped('data_id').unlink()
|
||||
return to_unlink.write({'purged': True})
|
||||
self.logger.info("Purging data entries: %s", to_unlink.mapped("name"))
|
||||
to_unlink.mapped("data_id").unlink()
|
||||
return to_unlink.write({"purged": True})
|
||||
|
||||
|
||||
class CleanupPurgeWizardData(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.wizard'
|
||||
_name = 'cleanup.purge.wizard.data'
|
||||
_description = 'Purge data'
|
||||
_inherit = "cleanup.purge.wizard"
|
||||
_name = "cleanup.purge.wizard.data"
|
||||
_description = "Purge data"
|
||||
|
||||
@api.model
|
||||
def find(self):
|
||||
|
@ -41,7 +44,7 @@ class CleanupPurgeWizardData(models.TransientModel):
|
|||
data_ids = []
|
||||
unknown_models = []
|
||||
self.env.cr.execute("""SELECT DISTINCT(model) FROM ir_model_data""")
|
||||
for model, in self.env.cr.fetchall():
|
||||
for (model,) in self.env.cr.fetchall():
|
||||
if not model:
|
||||
continue
|
||||
if model not in self.env:
|
||||
|
@ -54,19 +57,35 @@ class CleanupPurgeWizardData(models.TransientModel):
|
|||
AND res_id IS NOT NULL
|
||||
AND NOT EXISTS (
|
||||
SELECT id FROM %s WHERE id=ir_model_data.res_id)
|
||||
""", (model, IdentifierAdapter(self.env[model]._table)))
|
||||
""",
|
||||
(model, IdentifierAdapter(self.env[model]._table)),
|
||||
)
|
||||
data_ids.extend(data_row for data_row, in self.env.cr.fetchall())
|
||||
data_ids += self.env['ir.model.data'].search([
|
||||
('model', 'in', unknown_models),
|
||||
]).ids
|
||||
for data in self.env['ir.model.data'].browse(data_ids):
|
||||
res.append((0, 0, {
|
||||
'data_id': data.id,
|
||||
'name': "%s.%s, object of type %s" % (
|
||||
data.module, data.name, data.model)}))
|
||||
data_ids += (
|
||||
self.env["ir.model.data"]
|
||||
.search(
|
||||
[
|
||||
("model", "in", unknown_models),
|
||||
]
|
||||
)
|
||||
.ids
|
||||
)
|
||||
for data in self.env["ir.model.data"].browse(data_ids):
|
||||
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 UserError(_('No orphaned data entries found'))
|
||||
raise UserError(_("No orphaned data entries found"))
|
||||
return res
|
||||
|
||||
purge_line_ids = fields.One2many(
|
||||
'cleanup.purge.line.data', 'wizard_id', 'Data to purge')
|
||||
"cleanup.purge.line.data", "wizard_id", "Data to purge"
|
||||
)
|
||||
|
|
|
@ -6,12 +6,13 @@ from odoo.exceptions import UserError
|
|||
|
||||
|
||||
class CleanupPurgeLineMenu(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.line'
|
||||
_name = 'cleanup.purge.line.menu'
|
||||
_inherit = "cleanup.purge.line"
|
||||
_name = "cleanup.purge.line.menu"
|
||||
|
||||
wizard_id = fields.Many2one(
|
||||
'cleanup.purge.wizard.menu', 'Purge Wizard', readonly=True)
|
||||
menu_id = fields.Many2one('ir.ui.menu', 'Menu entry')
|
||||
"cleanup.purge.wizard.menu", "Purge Wizard", readonly=True
|
||||
)
|
||||
menu_id = fields.Many2one("ir.ui.menu", "Menu entry")
|
||||
|
||||
@api.multi
|
||||
def purge(self):
|
||||
|
@ -19,18 +20,19 @@ class CleanupPurgeLineMenu(models.TransientModel):
|
|||
if self:
|
||||
objs = self
|
||||
else:
|
||||
objs = self.env['cleanup.purge.line.menu']\
|
||||
.browse(self._context.get('active_ids'))
|
||||
objs = self.env["cleanup.purge.line.menu"].browse(
|
||||
self._context.get("active_ids")
|
||||
)
|
||||
to_unlink = objs.filtered(lambda x: not x.purged and x.menu_id)
|
||||
self.logger.info('Purging menu entries: %s', to_unlink.mapped('name'))
|
||||
to_unlink.mapped('menu_id').unlink()
|
||||
return to_unlink.write({'purged': True})
|
||||
self.logger.info("Purging menu entries: %s", to_unlink.mapped("name"))
|
||||
to_unlink.mapped("menu_id").unlink()
|
||||
return to_unlink.write({"purged": True})
|
||||
|
||||
|
||||
class CleanupPurgeWizardMenu(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.wizard'
|
||||
_name = 'cleanup.purge.wizard.menu'
|
||||
_description = 'Purge menus'
|
||||
_inherit = "cleanup.purge.wizard"
|
||||
_name = "cleanup.purge.wizard.menu"
|
||||
_description = "Purge menus"
|
||||
|
||||
@api.model
|
||||
def find(self):
|
||||
|
@ -38,21 +40,30 @@ class CleanupPurgeWizardMenu(models.TransientModel):
|
|||
Search for models that cannot be instantiated.
|
||||
"""
|
||||
res = []
|
||||
for menu in self.env['ir.ui.menu'].with_context(active_test=False)\
|
||||
.search([('action', '!=', False)]):
|
||||
if menu.action.type != 'ir.actions.act_window':
|
||||
for menu in (
|
||||
self.env["ir.ui.menu"]
|
||||
.with_context(active_test=False)
|
||||
.search([("action", "!=", False)])
|
||||
):
|
||||
if menu.action.type != "ir.actions.act_window":
|
||||
continue
|
||||
if (menu.action.res_model and menu.action.res_model not in
|
||||
self.env) or \
|
||||
(menu.action.src_model and menu.action.src_model not in
|
||||
self.env):
|
||||
res.append((0, 0, {
|
||||
'name': menu.complete_name,
|
||||
'menu_id': menu.id,
|
||||
}))
|
||||
if (menu.action.res_model and menu.action.res_model not in self.env) or (
|
||||
menu.action.src_model and menu.action.src_model not in self.env
|
||||
):
|
||||
res.append(
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"name": menu.complete_name,
|
||||
"menu_id": menu.id,
|
||||
},
|
||||
)
|
||||
)
|
||||
if not res:
|
||||
raise UserError(_('No dangling menu entries found'))
|
||||
raise UserError(_("No dangling menu entries found"))
|
||||
return res
|
||||
|
||||
purge_line_ids = fields.One2many(
|
||||
'cleanup.purge.line.menu', 'wizard_id', 'Menus to purge')
|
||||
"cleanup.purge.line.menu", "wizard_id", "Menus to purge"
|
||||
)
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
# Copyright 2014-2016 Therp BV <http://therp.nl>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
# pylint: disable=consider-merging-classes-inherited
|
||||
from odoo import _, api, models, fields
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG
|
||||
|
||||
|
||||
class IrModel(models.Model):
|
||||
_inherit = 'ir.model'
|
||||
_inherit = "ir.model"
|
||||
|
||||
def _drop_table(self):
|
||||
"""this function crashes for undefined models"""
|
||||
|
@ -22,7 +23,7 @@ class IrModel(models.Model):
|
|||
|
||||
|
||||
class IrModelFields(models.Model):
|
||||
_inherit = 'ir.model.fields'
|
||||
_inherit = "ir.model.fields"
|
||||
|
||||
@api.multi
|
||||
def _prepare_update(self):
|
||||
|
@ -32,12 +33,13 @@ class IrModelFields(models.Model):
|
|||
|
||||
|
||||
class CleanupPurgeLineModel(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.line'
|
||||
_name = 'cleanup.purge.line.model'
|
||||
_description = 'Purge models'
|
||||
_inherit = "cleanup.purge.line"
|
||||
_name = "cleanup.purge.line.model"
|
||||
_description = "Purge models"
|
||||
|
||||
wizard_id = fields.Many2one(
|
||||
'cleanup.purge.wizard.model', 'Purge Wizard', readonly=True)
|
||||
"cleanup.purge.wizard.model", "Purge Wizard", readonly=True
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def purge(self):
|
||||
|
@ -46,36 +48,45 @@ class CleanupPurgeLineModel(models.TransientModel):
|
|||
"""
|
||||
context_flags = {
|
||||
MODULE_UNINSTALL_FLAG: True,
|
||||
'purge': True,
|
||||
"purge": True,
|
||||
}
|
||||
|
||||
if self:
|
||||
objs = self
|
||||
else:
|
||||
objs = self.env['cleanup.purge.line.model']\
|
||||
.browse(self._context.get('active_ids'))
|
||||
objs = self.env["cleanup.purge.line.model"].browse(
|
||||
self._context.get("active_ids")
|
||||
)
|
||||
for line in objs:
|
||||
self.env.cr.execute(
|
||||
"SELECT id, model from ir_model WHERE model = %s",
|
||||
(line.name,))
|
||||
"SELECT id, model from ir_model WHERE model = %s", (line.name,)
|
||||
)
|
||||
row = self.env.cr.fetchone()
|
||||
if not row:
|
||||
continue
|
||||
self.logger.info('Purging model %s', row[1])
|
||||
attachments = self.env['ir.attachment'].search([
|
||||
('res_model', '=', line.name)
|
||||
])
|
||||
self.logger.info("Purging model %s", row[1])
|
||||
attachments = self.env["ir.attachment"].search(
|
||||
[("res_model", "=", line.name)]
|
||||
)
|
||||
if attachments:
|
||||
self.env.cr.execute(
|
||||
"UPDATE ir_attachment SET res_model = NULL "
|
||||
"WHERE id in %s",
|
||||
(tuple(attachments.ids), ))
|
||||
self.env['ir.model.constraint'].search([
|
||||
('model', '=', line.name),
|
||||
]).unlink()
|
||||
relations = self.env['ir.model.fields'].search([
|
||||
('relation', '=', row[1]),
|
||||
]).with_context(**context_flags)
|
||||
"UPDATE ir_attachment SET res_model = NULL " "WHERE id in %s",
|
||||
(tuple(attachments.ids),),
|
||||
)
|
||||
self.env["ir.model.constraint"].search(
|
||||
[
|
||||
("model", "=", line.name),
|
||||
]
|
||||
).unlink()
|
||||
relations = (
|
||||
self.env["ir.model.fields"]
|
||||
.search(
|
||||
[
|
||||
("relation", "=", row[1]),
|
||||
]
|
||||
)
|
||||
.with_context(**context_flags)
|
||||
)
|
||||
for relation in relations:
|
||||
try:
|
||||
# Fails if the model on the target side
|
||||
|
@ -85,19 +96,18 @@ class CleanupPurgeLineModel(models.TransientModel):
|
|||
pass
|
||||
except AttributeError:
|
||||
pass
|
||||
self.env['ir.model.relation'].search([
|
||||
('model', '=', line.name)
|
||||
]).with_context(**context_flags).unlink()
|
||||
self.env['ir.model'].browse([row[0]])\
|
||||
.with_context(**context_flags).unlink()
|
||||
line.write({'purged': True})
|
||||
self.env["ir.model.relation"].search(
|
||||
[("model", "=", line.name)]
|
||||
).with_context(**context_flags).unlink()
|
||||
self.env["ir.model"].browse([row[0]]).with_context(**context_flags).unlink()
|
||||
line.write({"purged": True})
|
||||
return True
|
||||
|
||||
|
||||
class CleanupPurgeWizardModel(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.wizard'
|
||||
_name = 'cleanup.purge.wizard.model'
|
||||
_description = 'Purge models'
|
||||
_inherit = "cleanup.purge.wizard"
|
||||
_name = "cleanup.purge.wizard.model"
|
||||
_description = "Purge models"
|
||||
|
||||
@api.model
|
||||
def find(self):
|
||||
|
@ -106,12 +116,13 @@ class CleanupPurgeWizardModel(models.TransientModel):
|
|||
"""
|
||||
res = []
|
||||
self.env.cr.execute("SELECT model from ir_model")
|
||||
for model, in self.env.cr.fetchall():
|
||||
for (model,) in self.env.cr.fetchall():
|
||||
if model not in self.env:
|
||||
res.append((0, 0, {'name': model}))
|
||||
res.append((0, 0, {"name": model}))
|
||||
if not res:
|
||||
raise UserError(_('No orphaned models found'))
|
||||
raise UserError(_("No orphaned models found"))
|
||||
return res
|
||||
|
||||
purge_line_ids = fields.One2many(
|
||||
'cleanup.purge.line.model', 'wizard_id', 'Models to purge')
|
||||
"cleanup.purge.line.model", "wizard_id", "Models to purge"
|
||||
)
|
||||
|
|
|
@ -4,35 +4,39 @@
|
|||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.modules.module import get_module_path
|
||||
|
||||
from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG
|
||||
|
||||
|
||||
class IrModelData(models.Model):
|
||||
_inherit = 'ir.model.data'
|
||||
_inherit = "ir.model.data"
|
||||
|
||||
@api.model
|
||||
def _module_data_uninstall(self, modules_to_remove):
|
||||
"""this function crashes for xmlids on undefined models or fields
|
||||
referring to undefined models"""
|
||||
for this in self.search([('module', 'in', modules_to_remove)]):
|
||||
if this.model == 'ir.model.fields':
|
||||
field = self.env[this.model].with_context(
|
||||
**{MODULE_UNINSTALL_FLAG: True}).browse(this.res_id)
|
||||
for this in self.search([("module", "in", modules_to_remove)]):
|
||||
if this.model == "ir.model.fields":
|
||||
field = (
|
||||
self.env[this.model]
|
||||
.with_context(**{MODULE_UNINSTALL_FLAG: True})
|
||||
.browse(this.res_id)
|
||||
)
|
||||
if not field.exists() or field.model not in self.env:
|
||||
this.unlink()
|
||||
continue
|
||||
if this.model not in self.env:
|
||||
this.unlink()
|
||||
return super(IrModelData, self)._module_data_uninstall(
|
||||
modules_to_remove)
|
||||
return super(IrModelData, self)._module_data_uninstall(modules_to_remove)
|
||||
|
||||
|
||||
class CleanupPurgeLineModule(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.line'
|
||||
_name = 'cleanup.purge.line.module'
|
||||
_inherit = "cleanup.purge.line"
|
||||
_name = "cleanup.purge.line.module"
|
||||
|
||||
wizard_id = fields.Many2one(
|
||||
'cleanup.purge.wizard.module', 'Purge Wizard', readonly=True)
|
||||
"cleanup.purge.wizard.module", "Purge Wizard", readonly=True
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def purge(self):
|
||||
|
@ -40,48 +44,46 @@ class CleanupPurgeLineModule(models.TransientModel):
|
|||
Uninstall modules upon manual confirmation, then reload
|
||||
the database.
|
||||
"""
|
||||
module_names = self.filtered(lambda x: not x.purged).mapped('name')
|
||||
modules = self.env['ir.module.module'].search([
|
||||
('name', 'in', module_names)
|
||||
])
|
||||
module_names = self.filtered(lambda x: not x.purged).mapped("name")
|
||||
modules = self.env["ir.module.module"].search([("name", "in", module_names)])
|
||||
if not modules:
|
||||
return True
|
||||
self.logger.info('Purging modules %s', ', '.join(module_names))
|
||||
self.logger.info("Purging modules %s", ", ".join(module_names))
|
||||
modules.filtered(
|
||||
lambda x: x.state not in ('uninstallable', 'uninstalled')
|
||||
lambda x: x.state not in ("uninstallable", "uninstalled")
|
||||
).button_immediate_uninstall()
|
||||
modules.refresh()
|
||||
modules.unlink()
|
||||
return self.write({'purged': True})
|
||||
return self.write({"purged": True})
|
||||
|
||||
|
||||
class CleanupPurgeWizardModule(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.wizard'
|
||||
_name = 'cleanup.purge.wizard.module'
|
||||
_description = 'Purge modules'
|
||||
_inherit = "cleanup.purge.wizard"
|
||||
_name = "cleanup.purge.wizard.module"
|
||||
_description = "Purge modules"
|
||||
|
||||
@api.model
|
||||
def find(self):
|
||||
res = []
|
||||
IrModule = self.env['ir.module.module']
|
||||
IrModule = self.env["ir.module.module"]
|
||||
for module in IrModule.search(
|
||||
[
|
||||
('to_buy', '=', False),
|
||||
('name', '!=', 'studio_customization')
|
||||
]
|
||||
[("to_buy", "=", False), ("name", "!=", "studio_customization")]
|
||||
):
|
||||
if get_module_path(module.name, display_warning=False):
|
||||
continue
|
||||
if module.state == 'uninstalled':
|
||||
self.env['cleanup.purge.line.module'].create({
|
||||
'name': module.name,
|
||||
}).purge()
|
||||
if module.state == "uninstalled":
|
||||
self.env["cleanup.purge.line.module"].create(
|
||||
{
|
||||
"name": module.name,
|
||||
}
|
||||
).purge()
|
||||
continue
|
||||
res.append((0, 0, {'name': module.name}))
|
||||
res.append((0, 0, {"name": module.name}))
|
||||
|
||||
if not res:
|
||||
raise UserError(_('No modules found to purge'))
|
||||
raise UserError(_("No modules found to purge"))
|
||||
return res
|
||||
|
||||
purge_line_ids = fields.One2many(
|
||||
'cleanup.purge.line.module', 'wizard_id', 'Modules to purge')
|
||||
"cleanup.purge.line.module", "wizard_id", "Modules to purge"
|
||||
)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# Copyright 2017 Therp BV <http://therp.nl>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
# pylint: disable=consider-merging-classes-inherited
|
||||
from odoo import api, models, fields
|
||||
from odoo import api, fields, models
|
||||
|
||||
REASON_DUPLICATE = 1
|
||||
REASON_DEFAULT = 2
|
||||
REASON_DEFAULT_FALSE = 3
|
||||
|
@ -9,31 +10,34 @@ REASON_UNKNOWN_MODEL = 4
|
|||
|
||||
|
||||
class CleanupPurgeLineProperty(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.line'
|
||||
_name = 'cleanup.purge.line.property'
|
||||
_description = 'Purge properties'
|
||||
_inherit = "cleanup.purge.line"
|
||||
_name = "cleanup.purge.line.property"
|
||||
_description = "Purge properties"
|
||||
|
||||
wizard_id = fields.Many2one(
|
||||
'cleanup.purge.wizard.property', 'Purge Wizard', readonly=True)
|
||||
property_id = fields.Many2one('ir.property')
|
||||
reason = fields.Selection([
|
||||
(REASON_DUPLICATE, 'Duplicated property'),
|
||||
(REASON_DEFAULT, 'Same value as default'),
|
||||
(REASON_DEFAULT_FALSE, 'Empty default property'),
|
||||
(REASON_UNKNOWN_MODEL, 'Unknown model'),
|
||||
])
|
||||
"cleanup.purge.wizard.property", "Purge Wizard", readonly=True
|
||||
)
|
||||
property_id = fields.Many2one("ir.property")
|
||||
reason = fields.Selection(
|
||||
[
|
||||
(REASON_DUPLICATE, "Duplicated property"),
|
||||
(REASON_DEFAULT, "Same value as default"),
|
||||
(REASON_DEFAULT_FALSE, "Empty default property"),
|
||||
(REASON_UNKNOWN_MODEL, "Unknown model"),
|
||||
]
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def purge(self):
|
||||
"""Delete properties"""
|
||||
self.write({'purged': True})
|
||||
return self.mapped('property_id').unlink()
|
||||
self.write({"purged": True})
|
||||
return self.mapped("property_id").unlink()
|
||||
|
||||
|
||||
class CleanupPurgeWizardProperty(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.wizard'
|
||||
_name = 'cleanup.purge.wizard.property'
|
||||
_description = 'Purge properties'
|
||||
_inherit = "cleanup.purge.wizard"
|
||||
_name = "cleanup.purge.wizard.property"
|
||||
_description = "Purge properties"
|
||||
|
||||
@api.model
|
||||
def find(self):
|
||||
|
@ -41,96 +45,118 @@ class CleanupPurgeWizardProperty(models.TransientModel):
|
|||
Search property records which are duplicated or the same as the default
|
||||
"""
|
||||
result = []
|
||||
default_properties = self.env['ir.property'].search([
|
||||
('res_id', '=', False),
|
||||
])
|
||||
default_properties = self.env["ir.property"].search(
|
||||
[
|
||||
("res_id", "=", False),
|
||||
]
|
||||
)
|
||||
handled_field_ids = []
|
||||
for prop in default_properties:
|
||||
value = None
|
||||
try:
|
||||
value = prop.get_by_record()
|
||||
except KeyError:
|
||||
result.append({
|
||||
'name': '%s@%s: %s' % (
|
||||
prop.name, prop.res_id, value,
|
||||
),
|
||||
'property_id': prop.id,
|
||||
'reason': REASON_UNKNOWN_MODEL,
|
||||
})
|
||||
result.append(
|
||||
{
|
||||
"name": "%s@%s: %s"
|
||||
% (
|
||||
prop.name,
|
||||
prop.res_id,
|
||||
value,
|
||||
),
|
||||
"property_id": prop.id,
|
||||
"reason": REASON_UNKNOWN_MODEL,
|
||||
}
|
||||
)
|
||||
continue
|
||||
if not value:
|
||||
result.append({
|
||||
'name': '%s@%s: %s' % (
|
||||
prop.name, prop.res_id, value,
|
||||
),
|
||||
'property_id': prop.id,
|
||||
'reason': REASON_DEFAULT_FALSE,
|
||||
})
|
||||
result.append(
|
||||
{
|
||||
"name": "%s@%s: %s"
|
||||
% (
|
||||
prop.name,
|
||||
prop.res_id,
|
||||
value,
|
||||
),
|
||||
"property_id": prop.id,
|
||||
"reason": REASON_DEFAULT_FALSE,
|
||||
}
|
||||
)
|
||||
continue
|
||||
if prop.fields_id.id in handled_field_ids:
|
||||
continue
|
||||
domain = [
|
||||
('id', '!=', prop.id),
|
||||
('fields_id', '=', prop.fields_id.id),
|
||||
("id", "!=", prop.id),
|
||||
("fields_id", "=", prop.fields_id.id),
|
||||
# =? explicitly tests for None or False, not falsyness
|
||||
('value_float', '=?', prop.value_float or False),
|
||||
('value_integer', '=?', prop.value_integer or False),
|
||||
('value_text', '=?', prop.value_text or False),
|
||||
('value_binary', '=?', prop.value_binary or False),
|
||||
('value_reference', '=?', prop.value_reference or False),
|
||||
('value_datetime', '=?', prop.value_datetime or False),
|
||||
("value_float", "=?", prop.value_float or False),
|
||||
("value_integer", "=?", prop.value_integer or False),
|
||||
("value_text", "=?", prop.value_text or False),
|
||||
("value_binary", "=?", prop.value_binary or False),
|
||||
("value_reference", "=?", prop.value_reference or False),
|
||||
("value_datetime", "=?", prop.value_datetime or False),
|
||||
]
|
||||
if prop.company_id:
|
||||
domain.append(('company_id', '=', prop.company_id.id))
|
||||
domain.append(("company_id", "=", prop.company_id.id))
|
||||
else:
|
||||
domain.extend([
|
||||
'|',
|
||||
('company_id', '=', False),
|
||||
(
|
||||
'company_id', 'in', self.env['res.company'].search([
|
||||
(
|
||||
'id', 'not in', default_properties.filtered(
|
||||
lambda x: x.company_id and
|
||||
x.fields_id == prop.fields_id
|
||||
).ids,
|
||||
domain.extend(
|
||||
[
|
||||
"|",
|
||||
("company_id", "=", False),
|
||||
(
|
||||
"company_id",
|
||||
"in",
|
||||
self.env["res.company"]
|
||||
.search(
|
||||
[
|
||||
(
|
||||
"id",
|
||||
"not in",
|
||||
default_properties.filtered(
|
||||
lambda x: x.company_id
|
||||
and x.fields_id == prop.fields_id
|
||||
).ids,
|
||||
)
|
||||
]
|
||||
)
|
||||
]).ids
|
||||
),
|
||||
])
|
||||
.ids,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
for redundant_property in self.env['ir.property'].search(domain):
|
||||
result.append({
|
||||
'name': '%s@%s: %s' % (
|
||||
prop.name, redundant_property.res_id,
|
||||
prop.get_by_record()
|
||||
),
|
||||
'property_id': redundant_property.id,
|
||||
'reason': REASON_DEFAULT,
|
||||
})
|
||||
for redundant_property in self.env["ir.property"].search(domain):
|
||||
result.append(
|
||||
{
|
||||
"name": "%s@%s: %s"
|
||||
% (prop.name, redundant_property.res_id, prop.get_by_record()),
|
||||
"property_id": redundant_property.id,
|
||||
"reason": REASON_DEFAULT,
|
||||
}
|
||||
)
|
||||
handled_field_ids.append(prop.fields_id.id)
|
||||
self.env.cr.execute(
|
||||
'''
|
||||
"""
|
||||
with grouped_properties(ids, cnt) as (
|
||||
select array_agg(id), count(*)
|
||||
from ir_property group by res_id, company_id, fields_id
|
||||
)
|
||||
select ids from grouped_properties where cnt > 1
|
||||
'''
|
||||
"""
|
||||
)
|
||||
for ids, in self.env.cr.fetchall():
|
||||
for (ids,) in self.env.cr.fetchall():
|
||||
# odoo uses the first property found by search
|
||||
for prop in self.env['ir.property'].search([
|
||||
('id', 'in', ids)
|
||||
])[1:]:
|
||||
result.append({
|
||||
'name': '%s@%s: %s' % (
|
||||
prop.name, prop.res_id, prop.get_by_record()
|
||||
),
|
||||
'property_id': prop.id,
|
||||
'reason': REASON_DUPLICATE,
|
||||
})
|
||||
for prop in self.env["ir.property"].search([("id", "in", ids)])[1:]:
|
||||
result.append(
|
||||
{
|
||||
"name": "%s@%s: %s"
|
||||
% (prop.name, prop.res_id, prop.get_by_record()),
|
||||
"property_id": prop.id,
|
||||
"reason": REASON_DUPLICATE,
|
||||
}
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
purge_line_ids = fields.One2many(
|
||||
'cleanup.purge.line.property', 'wizard_id', 'Properties to purge')
|
||||
"cleanup.purge.line.property", "wizard_id", "Properties to purge"
|
||||
)
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
# Copyright 2014-2016 Therp BV <http://therp.nl>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
# pylint: disable=consider-merging-classes-inherited
|
||||
from odoo import api, fields, models, _
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
from ..identifier_adapter import IdentifierAdapter
|
||||
|
||||
|
||||
class CleanupPurgeLineTable(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.line'
|
||||
_name = 'cleanup.purge.line.table'
|
||||
_description = 'Purge tables wizard lines'
|
||||
_inherit = "cleanup.purge.line"
|
||||
_name = "cleanup.purge.line.table"
|
||||
_description = "Purge tables wizard lines"
|
||||
|
||||
wizard_id = fields.Many2one(
|
||||
'cleanup.purge.wizard.table', 'Purge Wizard', readonly=True)
|
||||
"cleanup.purge.wizard.table", "Purge Wizard", readonly=True
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def purge(self):
|
||||
|
@ -22,9 +24,10 @@ class CleanupPurgeLineTable(models.TransientModel):
|
|||
if self:
|
||||
objs = self
|
||||
else:
|
||||
objs = self.env['cleanup.purge.line.table']\
|
||||
.browse(self._context.get('active_ids'))
|
||||
tables = objs.mapped('name')
|
||||
objs = self.env["cleanup.purge.line.table"].browse(
|
||||
self._context.get("active_ids")
|
||||
)
|
||||
tables = objs.mapped("name")
|
||||
for line in objs:
|
||||
if line.purged:
|
||||
continue
|
||||
|
@ -49,32 +52,35 @@ class CleanupPurgeLineTable(models.TransientModel):
|
|||
WHERE af.attnum = confkey AND af.attrelid = confrelid AND
|
||||
a.attnum = conkey AND a.attrelid = conrelid
|
||||
AND confrelid::regclass = '%s'::regclass;
|
||||
""", (IdentifierAdapter(line.name, quote=False),))
|
||||
""",
|
||||
(IdentifierAdapter(line.name, quote=False),),
|
||||
)
|
||||
|
||||
for constraint in self.env.cr.fetchall():
|
||||
if constraint[3] in tables:
|
||||
self.logger.info(
|
||||
'Dropping constraint %s on table %s (to be dropped)',
|
||||
constraint[0], constraint[3])
|
||||
"Dropping constraint %s on table %s (to be dropped)",
|
||||
constraint[0],
|
||||
constraint[3],
|
||||
)
|
||||
self.env.cr.execute(
|
||||
"ALTER TABLE %s DROP CONSTRAINT %s",
|
||||
(
|
||||
IdentifierAdapter(constraint[3]),
|
||||
IdentifierAdapter(constraint[0])
|
||||
))
|
||||
IdentifierAdapter(constraint[0]),
|
||||
),
|
||||
)
|
||||
|
||||
self.logger.info(
|
||||
'Dropping table %s', line.name)
|
||||
self.env.cr.execute(
|
||||
"DROP TABLE %s", (IdentifierAdapter(line.name),))
|
||||
line.write({'purged': True})
|
||||
self.logger.info("Dropping table %s", line.name)
|
||||
self.env.cr.execute("DROP TABLE %s", (IdentifierAdapter(line.name),))
|
||||
line.write({"purged": True})
|
||||
return True
|
||||
|
||||
|
||||
class CleanupPurgeWizardTable(models.TransientModel):
|
||||
_inherit = 'cleanup.purge.wizard'
|
||||
_name = 'cleanup.purge.wizard.table'
|
||||
_description = 'Purge tables'
|
||||
_inherit = "cleanup.purge.wizard"
|
||||
_name = "cleanup.purge.wizard.table"
|
||||
_description = "Purge tables"
|
||||
|
||||
@api.model
|
||||
def find(self):
|
||||
|
@ -83,7 +89,7 @@ class CleanupPurgeWizardTable(models.TransientModel):
|
|||
Ignore views for now.
|
||||
"""
|
||||
known_tables = []
|
||||
for model in self.env['ir.model'].search([]):
|
||||
for model in self.env["ir.model"].search([]):
|
||||
if model.model not in self.env:
|
||||
continue
|
||||
model_pool = self.env[model.model]
|
||||
|
@ -91,8 +97,8 @@ class CleanupPurgeWizardTable(models.TransientModel):
|
|||
known_tables += [
|
||||
column.relation
|
||||
for column in model_pool._fields.values()
|
||||
if column.type == 'many2many' and
|
||||
(column.compute is None or column.store)
|
||||
if column.type == "many2many"
|
||||
and (column.compute is None or column.store)
|
||||
and column.relation
|
||||
]
|
||||
|
||||
|
@ -100,12 +106,15 @@ class CleanupPurgeWizardTable(models.TransientModel):
|
|||
"""
|
||||
SELECT table_name FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
|
||||
AND table_name NOT IN %s""", (tuple(known_tables),))
|
||||
AND table_name NOT IN %s""",
|
||||
(tuple(known_tables),),
|
||||
)
|
||||
|
||||
res = [(0, 0, {'name': row[0]}) for row in self.env.cr.fetchall()]
|
||||
res = [(0, 0, {"name": row[0]}) for row in self.env.cr.fetchall()]
|
||||
if not res:
|
||||
raise UserError(_('No orphaned tables found'))
|
||||
raise UserError(_("No orphaned tables found"))
|
||||
return res
|
||||
|
||||
purge_line_ids = fields.One2many(
|
||||
'cleanup.purge.line.table', 'wizard_id', 'Tables to purge')
|
||||
"cleanup.purge.line.table", "wizard_id", "Tables to purge"
|
||||
)
|
||||
|
|
|
@ -3,21 +3,23 @@
|
|||
# pylint: disable=consider-merging-classes-inherited
|
||||
|
||||
import logging
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import AccessDenied
|
||||
|
||||
|
||||
class CleanupPurgeLine(models.AbstractModel):
|
||||
""" Abstract base class for the purge wizard lines """
|
||||
_name = 'cleanup.purge.line'
|
||||
_order = 'name'
|
||||
_description = 'Purge Column Abstract Wizard'
|
||||
|
||||
name = fields.Char('Name', readonly=True)
|
||||
purged = fields.Boolean('Purged', readonly=True)
|
||||
wizard_id = fields.Many2one('cleanup.purge.wizard')
|
||||
_name = "cleanup.purge.line"
|
||||
_order = "name"
|
||||
_description = "Purge Column Abstract Wizard"
|
||||
|
||||
logger = logging.getLogger('odoo.addons.database_cleanup')
|
||||
name = fields.Char("Name", readonly=True)
|
||||
purged = fields.Boolean("Purged", readonly=True)
|
||||
wizard_id = fields.Many2one("cleanup.purge.wizard")
|
||||
|
||||
logger = logging.getLogger("odoo.addons.database_cleanup")
|
||||
|
||||
@api.multi
|
||||
def purge(self):
|
||||
|
@ -26,22 +28,22 @@ class CleanupPurgeLine(models.AbstractModel):
|
|||
@api.model
|
||||
def create(self, values):
|
||||
# make sure the user trying this is actually supposed to do it
|
||||
if self.env.ref(
|
||||
'base.group_erp_manager') not in self.env.user.groups_id:
|
||||
if self.env.ref("base.group_erp_manager") not in self.env.user.groups_id:
|
||||
raise AccessDenied
|
||||
return super(CleanupPurgeLine, self).create(values)
|
||||
|
||||
|
||||
class PurgeWizard(models.AbstractModel):
|
||||
""" Abstract base class for the purge wizards """
|
||||
_name = 'cleanup.purge.wizard'
|
||||
_description = 'Purge stuff'
|
||||
|
||||
_name = "cleanup.purge.wizard"
|
||||
_description = "Purge stuff"
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
res = super(PurgeWizard, self).default_get(fields_list)
|
||||
if 'purge_line_ids' in fields_list:
|
||||
res['purge_line_ids'] = self.find()
|
||||
if "purge_line_ids" in fields_list:
|
||||
res["purge_line_ids"] = self.find()
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
|
@ -50,47 +52,43 @@ class PurgeWizard(models.AbstractModel):
|
|||
|
||||
@api.multi
|
||||
def purge_all(self):
|
||||
self.mapped('purge_line_ids').purge()
|
||||
self.mapped("purge_line_ids").purge()
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def get_wizard_action(self):
|
||||
wizard = self.create({})
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': wizard.display_name,
|
||||
'views': [(False, 'form')],
|
||||
'res_model': self._name,
|
||||
'res_id': wizard.id,
|
||||
'flags': {
|
||||
'action_buttons': False,
|
||||
'sidebar': False,
|
||||
"type": "ir.actions.act_window",
|
||||
"name": wizard.display_name,
|
||||
"views": [(False, "form")],
|
||||
"res_model": self._name,
|
||||
"res_id": wizard.id,
|
||||
"flags": {
|
||||
"action_buttons": False,
|
||||
"sidebar": False,
|
||||
},
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def select_lines(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Select lines to purge'),
|
||||
'views': [(False, 'tree'), (False, 'form')],
|
||||
'res_model': self._fields['purge_line_ids'].comodel_name,
|
||||
'domain': [('wizard_id', 'in', self.ids)],
|
||||
"type": "ir.actions.act_window",
|
||||
"name": _("Select lines to purge"),
|
||||
"views": [(False, "tree"), (False, "form")],
|
||||
"res_model": self._fields["purge_line_ids"].comodel_name,
|
||||
"domain": [("wizard_id", "in", self.ids)],
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def name_get(self):
|
||||
return [
|
||||
(this.id, self._description)
|
||||
for this in self
|
||||
]
|
||||
return [(this.id, self._description) for this in self]
|
||||
|
||||
@api.model
|
||||
def create(self, values):
|
||||
# make sure the user trying this is actually supposed to do it
|
||||
if self.env.ref(
|
||||
'base.group_erp_manager') not in self.env.user.groups_id:
|
||||
if self.env.ref("base.group_erp_manager") not in self.env.user.groups_id:
|
||||
raise AccessDenied
|
||||
return super(PurgeWizard, self).create(values)
|
||||
|
||||
purge_line_ids = fields.One2many('cleanup.purge.line', 'wizard_id')
|
||||
purge_line_ids = fields.One2many("cleanup.purge.line", "wizard_id")
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
# Copyright 2016 Therp BV <http://therp.nl>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from psycopg2 import ProgrammingError
|
||||
|
||||
from odoo.modules.registry import Registry
|
||||
from odoo.tools import config, mute_logger
|
||||
from odoo.tests.common import TransactionCase, at_install, post_install
|
||||
from odoo.tools import config, mute_logger
|
||||
|
||||
|
||||
# Use post_install to get all models loaded more info: odoo/odoo#13458
|
||||
|
@ -15,103 +16,129 @@ class TestDatabaseCleanup(TransactionCase):
|
|||
self.module = None
|
||||
self.model = None
|
||||
# Create one property for tests
|
||||
self.env['ir.property'].create({
|
||||
'fields_id': self.env.ref('base.field_res_partner__name').id,
|
||||
'type': 'char',
|
||||
'value_text': 'My default partner name',
|
||||
})
|
||||
self.env["ir.property"].create(
|
||||
{
|
||||
"fields_id": self.env.ref("base.field_res_partner__name").id,
|
||||
"type": "char",
|
||||
"value_text": "My default partner name",
|
||||
}
|
||||
)
|
||||
|
||||
def test_database_cleanup(self):
|
||||
# delete some index and check if our module recreated it
|
||||
self.env.cr.execute('drop index res_partner_name_index')
|
||||
create_indexes = self.env['cleanup.create_indexes.wizard'].create({})
|
||||
self.env.cr.execute("drop index res_partner_name_index")
|
||||
create_indexes = self.env["cleanup.create_indexes.wizard"].create({})
|
||||
create_indexes.purge_all()
|
||||
self.env.cr.execute(
|
||||
'select indexname from pg_indexes '
|
||||
"select indexname from pg_indexes "
|
||||
"where indexname='res_partner_name_index' and "
|
||||
"tablename='res_partner'"
|
||||
)
|
||||
self.assertEqual(self.env.cr.rowcount, 1)
|
||||
# duplicate a property
|
||||
duplicate_property = self.env['ir.property'].search([], limit=1).copy()
|
||||
purge_property = self.env['cleanup.purge.wizard.property'].create({})
|
||||
duplicate_property = self.env["ir.property"].search([], limit=1).copy()
|
||||
purge_property = self.env["cleanup.purge.wizard.property"].create({})
|
||||
purge_property.purge_all()
|
||||
self.assertFalse(duplicate_property.exists())
|
||||
# create an orphaned column
|
||||
self.env.cr.execute(
|
||||
'alter table res_partner add column database_cleanup_test int')
|
||||
"alter table res_partner add column database_cleanup_test int"
|
||||
)
|
||||
# We need use a model that is not blocked (Avoid use res.users)
|
||||
partner_model = self.env['ir.model'].search([
|
||||
('model', '=', 'res.partner')], limit=1)
|
||||
purge_columns = self.env['cleanup.purge.wizard.column'].create({
|
||||
'purge_line_ids': [(0, 0, {
|
||||
'model_id': partner_model.id, 'name': 'database_cleanup_test'}
|
||||
)]})
|
||||
partner_model = self.env["ir.model"].search(
|
||||
[("model", "=", "res.partner")], limit=1
|
||||
)
|
||||
purge_columns = self.env["cleanup.purge.wizard.column"].create(
|
||||
{
|
||||
"purge_line_ids": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{"model_id": partner_model.id, "name": "database_cleanup_test"},
|
||||
)
|
||||
]
|
||||
}
|
||||
)
|
||||
purge_columns.purge_all()
|
||||
# must be removed by the wizard
|
||||
with self.assertRaises(ProgrammingError):
|
||||
with self.env.registry.cursor() as cr:
|
||||
with mute_logger('odoo.sql_db'):
|
||||
cr.execute('select database_cleanup_test from res_partner')
|
||||
with mute_logger("odoo.sql_db"):
|
||||
cr.execute("select database_cleanup_test from res_partner")
|
||||
|
||||
# create a data entry pointing nowhere
|
||||
self.env.cr.execute('select max(id) + 1 from res_users')
|
||||
self.env['ir.model.data'].create({
|
||||
'module': 'database_cleanup',
|
||||
'name': 'test_no_data_entry',
|
||||
'model': 'res.users',
|
||||
'res_id': self.env.cr.fetchone()[0],
|
||||
})
|
||||
purge_data = self.env['cleanup.purge.wizard.data'].create({})
|
||||
self.env.cr.execute("select max(id) + 1 from res_users")
|
||||
self.env["ir.model.data"].create(
|
||||
{
|
||||
"module": "database_cleanup",
|
||||
"name": "test_no_data_entry",
|
||||
"model": "res.users",
|
||||
"res_id": self.env.cr.fetchone()[0],
|
||||
}
|
||||
)
|
||||
purge_data = self.env["cleanup.purge.wizard.data"].create({})
|
||||
purge_data.purge_all()
|
||||
# must be removed by the wizard
|
||||
with self.assertRaises(ValueError):
|
||||
self.env.ref('database_cleanup.test_no_data_entry')
|
||||
self.env.ref("database_cleanup.test_no_data_entry")
|
||||
|
||||
# create a nonexistent model
|
||||
self.model = self.env['ir.model'].create({
|
||||
'name': 'Database cleanup test model',
|
||||
'model': 'x_database.cleanup.test.model',
|
||||
})
|
||||
self.model = self.env["ir.model"].create(
|
||||
{
|
||||
"name": "Database cleanup test model",
|
||||
"model": "x_database.cleanup.test.model",
|
||||
}
|
||||
)
|
||||
self.env.cr.execute(
|
||||
'insert into ir_attachment (name, res_model, res_id, type) values '
|
||||
"('test attachment', 'database.cleanup.test.model', 42, 'binary')")
|
||||
self.env.registry.models.pop('x_database.cleanup.test.model')
|
||||
purge_models = self.env['cleanup.purge.wizard.model'].create({})
|
||||
"insert into ir_attachment (name, res_model, res_id, type) values "
|
||||
"('test attachment', 'database.cleanup.test.model', 42, 'binary')"
|
||||
)
|
||||
self.env.registry.models.pop("x_database.cleanup.test.model")
|
||||
purge_models = self.env["cleanup.purge.wizard.model"].create({})
|
||||
purge_models.purge_all()
|
||||
# must be removed by the wizard
|
||||
self.assertFalse(self.env['ir.model'].search([
|
||||
('model', '=', 'x_database.cleanup.test.model'),
|
||||
]))
|
||||
self.assertFalse(
|
||||
self.env["ir.model"].search(
|
||||
[
|
||||
("model", "=", "x_database.cleanup.test.model"),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
# create a nonexistent module
|
||||
self.module = self.env['ir.module.module'].create({
|
||||
'name': 'database_cleanup_test',
|
||||
'state': 'to upgrade',
|
||||
})
|
||||
purge_modules = self.env['cleanup.purge.wizard.module'].create({})
|
||||
self.module = self.env["ir.module.module"].create(
|
||||
{
|
||||
"name": "database_cleanup_test",
|
||||
"state": "to upgrade",
|
||||
}
|
||||
)
|
||||
purge_modules = self.env["cleanup.purge.wizard.module"].create({})
|
||||
# this reloads our registry, and we don't want to run tests twice
|
||||
# we also need the original registry for further tests, so save a
|
||||
# reference to it
|
||||
original_registry = Registry.registries[self.env.cr.dbname]
|
||||
config.options['test_enable'] = False
|
||||
config.options["test_enable"] = False
|
||||
purge_modules.purge_all()
|
||||
config.options['test_enable'] = True
|
||||
config.options["test_enable"] = True
|
||||
# must be removed by the wizard
|
||||
self.assertFalse(self.env['ir.module.module'].search([
|
||||
('name', '=', 'database_cleanup_test'),
|
||||
]))
|
||||
self.assertFalse(
|
||||
self.env["ir.module.module"].search(
|
||||
[
|
||||
("name", "=", "database_cleanup_test"),
|
||||
]
|
||||
)
|
||||
)
|
||||
# reset afterwards
|
||||
Registry.registries[self.env.cr.dbname] = original_registry
|
||||
|
||||
# create an orphaned table
|
||||
self.env.cr.execute('create table database_cleanup_test (test int)')
|
||||
purge_tables = self.env['cleanup.purge.wizard.table'].create({})
|
||||
self.env.cr.execute("create table database_cleanup_test (test int)")
|
||||
purge_tables = self.env["cleanup.purge.wizard.table"].create({})
|
||||
purge_tables.purge_all()
|
||||
with self.assertRaises(ProgrammingError):
|
||||
with self.env.registry.cursor() as cr:
|
||||
with mute_logger('odoo.sql_db'):
|
||||
cr.execute('select * from database_cleanup_test')
|
||||
with mute_logger("odoo.sql_db"):
|
||||
cr.execute("select * from database_cleanup_test")
|
||||
|
||||
def tearDown(self):
|
||||
super(TestDatabaseCleanup, self).tearDown()
|
||||
|
@ -120,10 +147,8 @@ class TestDatabaseCleanup(TransactionCase):
|
|||
self.env.cr.rollback()
|
||||
if self.module:
|
||||
cr2.execute(
|
||||
"DELETE FROM ir_module_module WHERE id=%s",
|
||||
(self.module.id,))
|
||||
"DELETE FROM ir_module_module WHERE id=%s", (self.module.id,)
|
||||
)
|
||||
if self.model:
|
||||
cr2.execute(
|
||||
"DELETE FROM ir_model WHERE id=%s",
|
||||
(self.model.id,))
|
||||
cr2.execute("DELETE FROM ir_model WHERE id=%s", (self.model.id,))
|
||||
cr2.commit()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="cleanup_create_indexes_wizard_view_form" model="ir.ui.view">
|
||||
<field name="model">cleanup.create_indexes.wizard</field>
|
||||
|
@ -18,8 +18,13 @@
|
|||
<field name="name">Create missing indexes</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="database_cleanup.model_cleanup_create_indexes_wizard" />
|
||||
<field name="code">action = env.get('cleanup.create_indexes.wizard').get_wizard_action()</field>
|
||||
<field
|
||||
name="model_id"
|
||||
ref="database_cleanup.model_cleanup_create_indexes_wizard"
|
||||
/>
|
||||
<field
|
||||
name="code"
|
||||
>action = env.get('cleanup.create_indexes.wizard').get_wizard_action()</field>
|
||||
</record>
|
||||
|
||||
<record id="cleanup_create_indexes_line_view_tree" model="ir.ui.view">
|
||||
|
@ -38,8 +43,14 @@
|
|||
<field name="name">Create</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="database_cleanup.model_cleanup_create_indexes_line" />
|
||||
<field
|
||||
name="model_id"
|
||||
ref="database_cleanup.model_cleanup_create_indexes_line"
|
||||
/>
|
||||
<field name="code">records.purge()</field>
|
||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_create_indexes_line" />
|
||||
<field
|
||||
name="binding_model_id"
|
||||
ref="database_cleanup.model_cleanup_create_indexes_line"
|
||||
/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
@ -1,66 +1,66 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<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"/>
|
||||
<field name="groups_id" eval="[(6,0, [ref('base.group_erp_manager')])]"/>
|
||||
<field name="parent_id" ref="base.menu_custom" />
|
||||
<field name="groups_id" eval="[(6,0, [ref('base.group_erp_manager')])]" />
|
||||
</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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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"/>
|
||||
<field name="parent_id" ref="menu_database_cleanup" />
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.menu" id="menu_purge_menus">
|
||||
<field name="name">Purge obsolete menu entries</field>
|
||||
<field name="sequence" eval="60" />
|
||||
<field name="action" ref="action_purge_menus" />
|
||||
<field name="parent_id" ref="menu_database_cleanup"/>
|
||||
<field name="parent_id" ref="menu_database_cleanup" />
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.menu" id="menu_create_indexes">
|
||||
<field name="name">Create missing indexes</field>
|
||||
<field name="sequence" eval="70" />
|
||||
<field name="action" ref="cleanup_create_indexes_wizard_action" />
|
||||
<field name="parent_id" ref="menu_database_cleanup"/>
|
||||
<field name="parent_id" ref="menu_database_cleanup" />
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.menu" id="menu_purge_property">
|
||||
<field name="name">Purge obsolete properties</field>
|
||||
<field name="sequence" eval="80" />
|
||||
<field name="action" ref="action_purge_property" />
|
||||
<field name="parent_id" ref="menu_database_cleanup"/>
|
||||
<field name="parent_id" ref="menu_database_cleanup" />
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="purge_columns_view" model="ir.ui.view">
|
||||
<field name="model">cleanup.purge.wizard.column</field>
|
||||
|
@ -15,7 +15,10 @@
|
|||
<field name="name">Purge columns</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_wizard_column" />
|
||||
<field
|
||||
name="model_id"
|
||||
ref="database_cleanup.model_cleanup_purge_wizard_column"
|
||||
/>
|
||||
<field name="code">
|
||||
action = env.get('cleanup.purge.wizard.column').get_wizard_action()
|
||||
</field>
|
||||
|
@ -38,6 +41,9 @@
|
|||
<field name="state">code</field>
|
||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_column" />
|
||||
<field name="code">records.purge()</field>
|
||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_purge_line_column" />
|
||||
<field
|
||||
name="binding_model_id"
|
||||
ref="database_cleanup.model_cleanup_purge_line_column"
|
||||
/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="purge_data_view" model="ir.ui.view">
|
||||
<field name="model">cleanup.purge.wizard.data</field>
|
||||
|
@ -38,6 +38,9 @@
|
|||
<field name="state">code</field>
|
||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_data" />
|
||||
<field name="code">records.purge()</field>
|
||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_purge_line_data" />
|
||||
<field
|
||||
name="binding_model_id"
|
||||
ref="database_cleanup.model_cleanup_purge_line_data"
|
||||
/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="purge_menus_view" model="ir.ui.view">
|
||||
<field name="model">cleanup.purge.wizard.menu</field>
|
||||
<field name="inherit_id" ref="form_purge_wizard" />
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<data/>
|
||||
<data />
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
@ -34,6 +34,9 @@
|
|||
<field name="state">code</field>
|
||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_menu" />
|
||||
<field name="code">records.purge()</field>
|
||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_purge_line_menu" />
|
||||
<field
|
||||
name="binding_model_id"
|
||||
ref="database_cleanup.model_cleanup_purge_line_menu"
|
||||
/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="purge_models_view" model="ir.ui.view">
|
||||
<field name="model">cleanup.purge.wizard.model</field>
|
||||
|
@ -13,7 +13,10 @@
|
|||
<field name="name">Purge models</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_wizard_model" />
|
||||
<field
|
||||
name="model_id"
|
||||
ref="database_cleanup.model_cleanup_purge_wizard_model"
|
||||
/>
|
||||
<field name="code">
|
||||
action = env.get('cleanup.purge.wizard.model').get_wizard_action()
|
||||
</field>
|
||||
|
@ -34,6 +37,9 @@
|
|||
<field name="state">code</field>
|
||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_model" />
|
||||
<field name="code">records.purge()</field>
|
||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_purge_line_model" />
|
||||
<field
|
||||
name="binding_model_id"
|
||||
ref="database_cleanup.model_cleanup_purge_line_model"
|
||||
/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="purge_modules_view" model="ir.ui.view">
|
||||
<field name="model">cleanup.purge.wizard.module</field>
|
||||
|
@ -13,7 +13,10 @@
|
|||
<field name="name">Purge modules</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_wizard_module" />
|
||||
<field
|
||||
name="model_id"
|
||||
ref="database_cleanup.model_cleanup_purge_wizard_module"
|
||||
/>
|
||||
<field name="code">
|
||||
action = env.get('cleanup.purge.wizard.module').get_wizard_action()
|
||||
</field>
|
||||
|
@ -24,7 +27,7 @@
|
|||
<field name="inherit_id" ref="tree_purge_line" />
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<data/>
|
||||
<data />
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
@ -34,6 +37,9 @@
|
|||
<field name="state">code</field>
|
||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_module" />
|
||||
<field name="code">records.purge()</field>
|
||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_purge_line_module" />
|
||||
<field
|
||||
name="binding_model_id"
|
||||
ref="database_cleanup.model_cleanup_purge_line_module"
|
||||
/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="purge_property_view" model="ir.ui.view">
|
||||
<field name="model">cleanup.purge.wizard.property</field>
|
||||
|
@ -13,8 +13,13 @@
|
|||
<field name="name">Purge properties</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_wizard_property" />
|
||||
<field name="code">action = env.get('cleanup.purge.wizard.property').get_wizard_action()</field>
|
||||
<field
|
||||
name="model_id"
|
||||
ref="database_cleanup.model_cleanup_purge_wizard_property"
|
||||
/>
|
||||
<field
|
||||
name="code"
|
||||
>action = env.get('cleanup.purge.wizard.property').get_wizard_action()</field>
|
||||
</record>
|
||||
|
||||
<record id="purge_property_line_tree" model="ir.ui.view">
|
||||
|
@ -32,8 +37,14 @@
|
|||
<field name="name">Purge</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_property" />
|
||||
<field
|
||||
name="model_id"
|
||||
ref="database_cleanup.model_cleanup_purge_line_property"
|
||||
/>
|
||||
<field name="code">records.purge()</field>
|
||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_purge_line_property" />
|
||||
<field
|
||||
name="binding_model_id"
|
||||
ref="database_cleanup.model_cleanup_purge_line_property"
|
||||
/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="purge_tables_view" model="ir.ui.view">
|
||||
<field name="model">cleanup.purge.wizard.table</field>
|
||||
|
@ -13,7 +13,10 @@
|
|||
<field name="name">Purge tables</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_wizard_table" />
|
||||
<field
|
||||
name="model_id"
|
||||
ref="database_cleanup.model_cleanup_purge_wizard_table"
|
||||
/>
|
||||
<field name="code">
|
||||
action = env.get('cleanup.purge.wizard.table').get_wizard_action()
|
||||
</field>
|
||||
|
@ -34,6 +37,9 @@
|
|||
<field name="state">code</field>
|
||||
<field name="model_id" ref="database_cleanup.model_cleanup_purge_line_table" />
|
||||
<field name="code">records.purge()</field>
|
||||
<field name="binding_model_id" ref="database_cleanup.model_cleanup_purge_line_table" />
|
||||
<field
|
||||
name="binding_model_id"
|
||||
ref="database_cleanup.model_cleanup_purge_line_table"
|
||||
/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
@ -1,26 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="form_purge_wizard" model="ir.ui.view">
|
||||
<field name="model">cleanup.purge.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
<button type="object" name="purge_all" string="Purge all" class="oe_highlight" />
|
||||
<button
|
||||
type="object"
|
||||
name="purge_all"
|
||||
string="Purge all"
|
||||
class="oe_highlight"
|
||||
/>
|
||||
<button type="object" name="select_lines" string="Select lines" />
|
||||
</header>
|
||||
<div attrs="{'invisible': [('purge_line_ids', '!=', [])]}">
|
||||
Nothing found to clean up.
|
||||
</div>
|
||||
<field name="purge_line_ids" attrs="{'invisible': [('purge_line_ids', '=', [])]}">
|
||||
<field
|
||||
name="purge_line_ids"
|
||||
attrs="{'invisible': [('purge_line_ids', '=', [])]}"
|
||||
>
|
||||
<form>
|
||||
<group>
|
||||
<field name="name" />
|
||||
<field name="purged" />
|
||||
</group>
|
||||
<footer>
|
||||
<button type="object" name="purge" class="oe_highlight"
|
||||
string="Purge"
|
||||
attrs="{'invisible': [('purged', '=', True)]}"/>
|
||||
<button
|
||||
type="object"
|
||||
name="purge"
|
||||
class="oe_highlight"
|
||||
string="Purge"
|
||||
attrs="{'invisible': [('purged', '=', True)]}"
|
||||
/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
|
@ -33,9 +45,13 @@
|
|||
<tree string="Purge models" delete="false" create="false">
|
||||
<field name="name" />
|
||||
<field name="purged" />
|
||||
<button type="object" name="purge"
|
||||
icon="fa-times-circle text-danger" string="Purge this model"
|
||||
attrs="{'invisible': [('purged', '=', True)]}"/>
|
||||
<button
|
||||
type="object"
|
||||
name="purge"
|
||||
icon="fa-times-circle text-danger"
|
||||
string="Purge this model"
|
||||
attrs="{'invisible': [('purged', '=', True)]}"
|
||||
/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
|
Loading…
Reference in New Issue