[MIG] database_cleanup: Migration to 14.0

pull/2684/head
Stephane Mangin 2021-01-12 14:28:34 +01:00 committed by Miika Nissi
parent d1acfa4b4a
commit 7d8967fe32
No known key found for this signature in database
GPG Key ID: B20DC9FCFAF92E7F
28 changed files with 411 additions and 222 deletions

View File

@ -1,10 +1,29 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
===================
Base Technical User
===================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github
:target: https://github.com/OCA/server-tools/tree/14.0/database_cleanup
:alt: OCA/server-tools
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-tools-14-0/server-tools-14-0-database_cleanup
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/149/14.0
:alt: Try me on Runbot
================
Database cleanup
================
|badge1| |badge2| |badge3| |badge4| |badge5|
Clean your Odoo database from remnants of modules, models, columns and
tables left by uninstalled modules (prior to 7.0) or a homebrew database
@ -16,6 +35,11 @@ with the technical details of the Odoo 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.
**Table of contents**
.. contents::
:local:
Usage
=====
@ -28,42 +52,46 @@ in one big step (if you are *really* confident).
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/149/11.0
:target: https://runbot.odoo-community.org/runbot/149/14.0
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/database_cleanup/issues>`_.
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/server-tools/issues/new?body=module:%20database_cleanup%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Images
------
Authors
~~~~~~~
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
* Therp BV
Contributors
------------
~~~~~~~~~~~~
* Stefan Rijnhart <stefan@opener.amsterdam>
* Holger Brunn <hbrunn@therp.nl>
* Stéphane Mangin <stephane.mangin@camptocamp.com>
Do not contact contributors directly about help with questions or problems concerning this addon, but use the `community mailing list <mailto:community@mail.odoo.com>`_ or the `appropriate specialized mailinglist <https://odoo-community.org/groups>`_ for help, and the bug tracker linked in `Bug Tracker`_ above for technical issues.
Maintainers
~~~~~~~~~~~
Maintainer
----------
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit https://odoo-community.org.
This module is part of the `OCA/server-tools <https://github.com/OCA/server-tools/tree/14.0/base_technical_user>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@ -1,9 +1,11 @@
# Copyright 2014-2016 Therp BV <http://therp.nl>
# Copyright 2021 Camptocamp SA <https://camptocamp.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
"name": "Database cleanup",
"version": "12.0.1.0.1",
"version": "14.0.1.0.0",
"author": "Therp BV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/server-tools",
"depends": ["base"],
"license": "AGPL-3",
"category": "Tools",
@ -18,6 +20,7 @@
"views/create_indexes.xml",
"views/purge_properties.xml",
"views/menu.xml",
"security/ir.model.access.csv",
],
"installable": True,
}

View File

@ -457,7 +457,7 @@ msgstr "Purge obsolete modules"
#, fuzzy
#| msgid "Purge obsolete models"
msgid "Purge obsolete properties"
msgstr "Purge obsolete models"
msgstr "Purge obsolete properties"
#. module: database_cleanup
#: model:ir.ui.menu,name:database_cleanup.menu_purge_tables

View File

@ -1,7 +1,8 @@
# Copyright 2017 Therp BV <http://therp.nl>
# Copyright 2021 Camptocamp <https://camptocamp.com>
# 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 fields, models
from ..identifier_adapter import IdentifierAdapter
@ -9,14 +10,13 @@ from ..identifier_adapter import IdentifierAdapter
class CreateIndexesLine(models.TransientModel):
_inherit = "cleanup.purge.line"
_name = "cleanup.create_indexes.line"
_description = "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)
@api.multi
def purge(self):
tables = set()
for field in self.mapped("field_id"):
model = self.env[field.model]
name = "{}_{}_index".format(model._table, field.name)
@ -28,8 +28,6 @@ class CreateIndexesLine(models.TransientModel):
IdentifierAdapter(field.name),
),
)
tables.add(model._table)
for table in tables:
self.env.cr.execute("analyze %s", (IdentifierAdapter(model._table),))
self.write(
{
@ -48,7 +46,6 @@ class CreateIndexesWizard(models.TransientModel):
"wizard_id",
)
@api.multi
def find(self):
res = list()
for field in self.env["ir.model.fields"].search(

View File

@ -1,4 +1,5 @@
# Copyright 2014-2016 Therp BV <http://therp.nl>
# Copyright 2021 Camptocamp <https://camptocamp.com>
# 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
@ -10,14 +11,13 @@ from ..identifier_adapter import IdentifierAdapter
class CleanupPurgeLineColumn(models.TransientModel):
_inherit = "cleanup.purge.line"
_name = "cleanup.purge.line.column"
_description = "Purge Column Wizard Lines"
_description = "Cleanup Purge Line Column"
model_id = fields.Many2one("ir.model", "Model", required=True, ondelete="CASCADE")
wizard_id = fields.Many2one(
"cleanup.purge.wizard.column", "Purge Wizard", readonly=True
)
@api.multi
def purge(self):
"""
Unlink columns upon manual confirmation.
@ -123,7 +123,7 @@ class CleanupPurgeWizardColumn(models.TransientModel):
model_pool
)
for table, model_spec in table2model.items():
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]}))
if not res:

View File

@ -1,4 +1,5 @@
# Copyright 2014-2016 Therp BV <http://therp.nl>
# Copyright 2021 Camptocamp <https://camptocamp.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import _, api, fields, models
from odoo.exceptions import UserError
@ -9,13 +10,13 @@ from ..identifier_adapter import IdentifierAdapter
class CleanupPurgeLineData(models.TransientModel):
_inherit = "cleanup.purge.line"
_name = "cleanup.purge.line.data"
_description = "Cleanup Purge Line Data"
data_id = fields.Many2one("ir.model.data", "Data entry")
wizard_id = fields.Many2one(
"cleanup.purge.wizard.data", "Purge Wizard", readonly=True
)
@api.multi
def purge(self):
"""Unlink data entries upon manual confirmation."""
if self:

View File

@ -1,4 +1,5 @@
# Copyright 2014-2016 Therp BV <http://therp.nl>
# Copyright 2021 Camptocamp <https://camptocamp.com>
# 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
@ -8,13 +9,13 @@ from odoo.exceptions import UserError
class CleanupPurgeLineMenu(models.TransientModel):
_inherit = "cleanup.purge.line"
_name = "cleanup.purge.line.menu"
_description = "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")
@api.multi
def purge(self):
"""Unlink menu entries upon manual confirmation."""
if self:
@ -47,9 +48,7 @@ class CleanupPurgeWizardMenu(models.TransientModel):
):
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
):
if menu.action.res_model and menu.action.res_model not in self.env:
res.append(
(
0,

View File

@ -1,4 +1,5 @@
# Copyright 2014-2016 Therp BV <http://therp.nl>
# Copyright 2021 Camptocamp <https://camptocamp.com>
# 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
@ -25,7 +26,6 @@ class IrModel(models.Model):
class IrModelFields(models.Model):
_inherit = "ir.model.fields"
@api.multi
def _prepare_update(self):
"""this function crashes for undefined models"""
existing = self.filtered(lambda x: x.model in self.env)
@ -35,13 +35,12 @@ class IrModelFields(models.Model):
class CleanupPurgeLineModel(models.TransientModel):
_inherit = "cleanup.purge.line"
_name = "cleanup.purge.line.model"
_description = "Purge models"
_description = "Cleanup Purge Line Model"
wizard_id = fields.Many2one(
"cleanup.purge.wizard.model", "Purge Wizard", readonly=True
)
@api.multi
def purge(self):
"""
Unlink models upon manual confirmation.

View File

@ -1,4 +1,5 @@
# Copyright 2014-2016 Therp BV <http://therp.nl>
# Copyright 2021 Camptocamp <https://camptocamp.com>
# 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
@ -33,12 +34,12 @@ class IrModelData(models.Model):
class CleanupPurgeLineModule(models.TransientModel):
_inherit = "cleanup.purge.line"
_name = "cleanup.purge.line.module"
_description = "Cleanup Purge Line Module"
wizard_id = fields.Many2one(
"cleanup.purge.wizard.module", "Purge Wizard", readonly=True
)
@api.multi
def purge(self):
"""
Uninstall modules upon manual confirmation, then reload

View File

@ -1,18 +1,19 @@
# Copyright 2017 Therp BV <http://therp.nl>
# Copyright 2021 Camptocamp <https://camptocamp.com>
# 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
REASON_DUPLICATE = 1
REASON_DEFAULT = 2
REASON_DEFAULT_FALSE = 3
REASON_UNKNOWN_MODEL = 4
REASON_DUPLICATE = "REASON_DUPLICATE"
REASON_DEFAULT = "REASON_DEFAULT"
REASON_DEFAULT_FALSE = "REASON_DEFAULT_FALSE"
REASON_UNKNOWN_MODEL = "REASON_UNKNOWN_MODEL"
class CleanupPurgeLineProperty(models.TransientModel):
_inherit = "cleanup.purge.line"
_name = "cleanup.purge.line.property"
_description = "Purge properties"
_description = "Cleanup Purge Line Property"
wizard_id = fields.Many2one(
"cleanup.purge.wizard.property", "Purge Wizard", readonly=True
@ -27,7 +28,6 @@ class CleanupPurgeLineProperty(models.TransientModel):
]
)
@api.multi
def purge(self):
"""Delete properties"""
self.write({"purged": True})
@ -58,12 +58,7 @@ class CleanupPurgeWizardProperty(models.TransientModel):
except KeyError:
result.append(
{
"name": "%s@%s: %s"
% (
prop.name,
prop.res_id,
value,
),
"name": "{}@{}: {}".format(prop.name, prop.res_id, value),
"property_id": prop.id,
"reason": REASON_UNKNOWN_MODEL,
}
@ -72,12 +67,7 @@ class CleanupPurgeWizardProperty(models.TransientModel):
if not value:
result.append(
{
"name": "%s@%s: %s"
% (
prop.name,
prop.res_id,
value,
),
"name": "{}@{}: {}".format(prop.name, prop.res_id, value),
"property_id": prop.id,
"reason": REASON_DEFAULT_FALSE,
}
@ -127,8 +117,9 @@ class CleanupPurgeWizardProperty(models.TransientModel):
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()),
"name": "{}@{}: {}".format(
prop.name, redundant_property.res_id, prop.get_by_record()
),
"property_id": redundant_property.id,
"reason": REASON_DEFAULT,
}
@ -148,8 +139,9 @@ class CleanupPurgeWizardProperty(models.TransientModel):
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()),
"name": "{}@{}: {}".format(
prop.name, prop.res_id, prop.get_by_record()
),
"property_id": prop.id,
"reason": REASON_DUPLICATE,
}

View File

@ -1,4 +1,5 @@
# Copyright 2014-2016 Therp BV <http://therp.nl>
# Copyright 2021 Camptocamp <https://camptocamp.com>
# 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
@ -10,13 +11,12 @@ from ..identifier_adapter import IdentifierAdapter
class CleanupPurgeLineTable(models.TransientModel):
_inherit = "cleanup.purge.line"
_name = "cleanup.purge.line.table"
_description = "Purge tables wizard lines"
_description = "Cleanup Purge Line Table"
wizard_id = fields.Many2one(
"cleanup.purge.wizard.table", "Purge Wizard", readonly=True
)
@api.multi
def purge(self):
"""
Unlink tables upon manual confirmation.

View File

@ -1,4 +1,5 @@
# Copyright 2014-2016 Therp BV <http://therp.nl>
# Copyright 2021 Camptocamp <https://camptocamp.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
# pylint: disable=consider-merging-classes-inherited
@ -21,7 +22,6 @@ class CleanupPurgeLine(models.AbstractModel):
logger = logging.getLogger("odoo.addons.database_cleanup")
@api.multi
def purge(self):
raise NotImplementedError
@ -46,11 +46,9 @@ class PurgeWizard(models.AbstractModel):
res["purge_line_ids"] = self.find()
return res
@api.multi
def find(self):
raise NotImplementedError
@api.multi
def purge_all(self):
self.mapped("purge_line_ids").purge()
return True
@ -70,7 +68,6 @@ class PurgeWizard(models.AbstractModel):
},
}
@api.multi
def select_lines(self):
return {
"type": "ir.actions.act_window",
@ -80,7 +77,6 @@ class PurgeWizard(models.AbstractModel):
"domain": [("wizard_id", "in", self.ids)],
}
@api.multi
def name_get(self):
return [(this.id, self._description) for this in self]

View File

@ -0,0 +1,3 @@
* Stefan Rijnhart <stefan@opener.amsterdam>
* Holger Brunn <hbrunn@therp.nl>
* Stéphane Mangin <stephane.mangin@camptocamp.com>

View File

@ -0,0 +1,9 @@
Clean your Odoo 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 Odoo.
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 Odoo 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.

View File

@ -0,0 +1,10 @@
After installation of this module, go to the Settings menu -> Technical ->
Database cleanup. This menu is only available to members of the *Access Rights*
group. 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).
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/149/11.0

View File

@ -0,0 +1,17 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_cleanup_create_indexes_line,access_cleanup_create_indexes_line,model_cleanup_create_indexes_line,base.group_user,1,1,1,1
access_cleanup_create_indexes_wizard,access_cleanup_create_indexes_wizard,model_cleanup_create_indexes_wizard,base.group_user,1,1,1,1
access_cleanup_purge_line_module,access_cleanup_purge_line_module,model_cleanup_purge_line_module,base.group_user,1,1,1,1
access_cleanup_purge_wizard_module,access_cleanup_purge_wizard_module,model_cleanup_purge_wizard_module,base.group_user,1,1,1,1
access_cleanup_purge_line_model,access_cleanup_purge_line_model,model_cleanup_purge_line_model,base.group_user,1,1,1,1
access_cleanup_purge_wizard_model,access_cleanup_purge_wizard_model,model_cleanup_purge_wizard_model,base.group_user,1,1,1,1
access_cleanup_purge_line_column,access_cleanup_purge_line_column,model_cleanup_purge_line_column,base.group_user,1,1,1,1
access_cleanup_purge_wizard_column,access_cleanup_purge_wizard_column,model_cleanup_purge_wizard_column,base.group_user,1,1,1,1
access_cleanup_purge_line_table,access_cleanup_purge_line_table,model_cleanup_purge_line_table,base.group_user,1,1,1,1
access_cleanup_purge_wizard_table,access_cleanup_purge_wizard_table,model_cleanup_purge_wizard_table,base.group_user,1,1,1,1
access_cleanup_purge_line_data,access_cleanup_purge_line_data,model_cleanup_purge_line_data,base.group_user,1,1,1,1
access_cleanup_purge_wizard_data,access_cleanup_purge_wizard_data,model_cleanup_purge_wizard_data,base.group_user,1,1,1,1
access_cleanup_purge_line_menu,access_cleanup_purge_line_menu,model_cleanup_purge_line_menu,base.group_user,1,1,1,1
access_cleanup_purge_wizard_menu,access_cleanup_purge_wizard_menu,model_cleanup_purge_wizard_menu,base.group_user,1,1,1,1
access_cleanup_purge_line_property,access_cleanup_purge_line_property,model_cleanup_purge_line_property,base.group_user,1,1,1,1
access_cleanup_purge_wizard_property,access_cleanup_purge_wizard_property,model_cleanup_purge_wizard_property,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_cleanup_create_indexes_line access_cleanup_create_indexes_line model_cleanup_create_indexes_line base.group_user 1 1 1 1
3 access_cleanup_create_indexes_wizard access_cleanup_create_indexes_wizard model_cleanup_create_indexes_wizard base.group_user 1 1 1 1
4 access_cleanup_purge_line_module access_cleanup_purge_line_module model_cleanup_purge_line_module base.group_user 1 1 1 1
5 access_cleanup_purge_wizard_module access_cleanup_purge_wizard_module model_cleanup_purge_wizard_module base.group_user 1 1 1 1
6 access_cleanup_purge_line_model access_cleanup_purge_line_model model_cleanup_purge_line_model base.group_user 1 1 1 1
7 access_cleanup_purge_wizard_model access_cleanup_purge_wizard_model model_cleanup_purge_wizard_model base.group_user 1 1 1 1
8 access_cleanup_purge_line_column access_cleanup_purge_line_column model_cleanup_purge_line_column base.group_user 1 1 1 1
9 access_cleanup_purge_wizard_column access_cleanup_purge_wizard_column model_cleanup_purge_wizard_column base.group_user 1 1 1 1
10 access_cleanup_purge_line_table access_cleanup_purge_line_table model_cleanup_purge_line_table base.group_user 1 1 1 1
11 access_cleanup_purge_wizard_table access_cleanup_purge_wizard_table model_cleanup_purge_wizard_table base.group_user 1 1 1 1
12 access_cleanup_purge_line_data access_cleanup_purge_line_data model_cleanup_purge_line_data base.group_user 1 1 1 1
13 access_cleanup_purge_wizard_data access_cleanup_purge_wizard_data model_cleanup_purge_wizard_data base.group_user 1 1 1 1
14 access_cleanup_purge_line_menu access_cleanup_purge_line_menu model_cleanup_purge_line_menu base.group_user 1 1 1 1
15 access_cleanup_purge_wizard_menu access_cleanup_purge_wizard_menu model_cleanup_purge_wizard_menu base.group_user 1 1 1 1
16 access_cleanup_purge_line_property access_cleanup_purge_line_property model_cleanup_purge_line_property base.group_user 1 1 1 1
17 access_cleanup_purge_wizard_property access_cleanup_purge_wizard_property model_cleanup_purge_wizard_property base.group_user 1 1 1 1

View File

@ -1,3 +1,12 @@
# Copyright 2016 Therp BV <http://therp.nl>
# Copyright 2021 Camptocamp
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import test_database_cleanup
from . import common
from . import test_create_indexes
from . import test_purge_columns
from . import test_purge_data
from . import test_purge_menus
from . import test_purge_models
from . import test_purge_modules
from . import test_purge_properties
from . import test_purge_tables

View File

@ -0,0 +1,24 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo.modules.registry import Registry
from odoo.tests import TransactionCase
from odoo.tests.common import tagged
# Use post_install to get all models loaded more info: odoo/odoo#13458
@tagged("post_install", "-at_install")
class Common(TransactionCase):
def setUp(self):
super(Common, self).setUp()
# 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
self.original_registry = Registry.registries[self.env.cr.dbname]
def tearDown(self):
super(Common, self).tearDown()
# Force rollback to avoid unstable test database
self.env.cr.rollback()
# reset afterwards
Registry.registries[self.env.cr.dbname] = self.original_registry

View File

@ -0,0 +1,20 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from .common import Common
class TestCreateIndexesLine(Common):
def setUp(self):
super(TestCreateIndexesLine, self).setUp()
# delete some index and check if our module recreated it
self.env.cr.execute("drop index res_partner_name_index")
def test_deleted_index(self):
wizard = self.env["cleanup.create_indexes.wizard"].create({})
wizard.purge_all()
self.env.cr.execute(
"select indexname from pg_indexes where "
"indexname='res_partner_name_index' and tablename='res_partner' "
)
self.assertEqual(self.env.cr.rowcount, 1)

View File

@ -1,154 +0,0 @@
# 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.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
@at_install(False)
@post_install(True)
class TestDatabaseCleanup(TransactionCase):
def setUp(self):
super(TestDatabaseCleanup, self).setUp()
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",
}
)
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({})
create_indexes.purge_all()
self.env.cr.execute(
"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({})
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"
)
# 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"},
)
]
}
)
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")
# 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({})
purge_data.purge_all()
# must be removed by the wizard
with self.assertRaises(ValueError):
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.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({})
purge_models.purge_all()
# must be removed by the wizard
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({})
# 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
purge_modules.purge_all()
config.options["test_enable"] = True
# must be removed by the wizard
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({})
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")
def tearDown(self):
super(TestDatabaseCleanup, self).tearDown()
with self.registry.cursor() as cr2:
# Release blocked tables with pending deletes
self.env.cr.rollback()
if self.module:
cr2.execute(
"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.commit()

View File

@ -0,0 +1,39 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from psycopg2 import ProgrammingError
from odoo.tools import mute_logger
from .common import Common
class TestCleanupPurgeLineColumn(Common):
def setUp(self):
super(TestCleanupPurgeLineColumn, self).setUp()
# create an orphaned column
self.env.cr.execute(
"alter table res_partner add column database_cleanup_test int"
)
def test_empty_column(self):
# 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
)
wizard = self.env["cleanup.purge.wizard.column"].create(
{
"purge_line_ids": [
(
0,
0,
{"model_id": partner_model.id, "name": "database_cleanup_test"},
)
]
}
)
wizard.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")

View File

@ -0,0 +1,26 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from .common import Common
class TestCleanupPurgeLineData(Common):
def setUp(self):
super(TestCleanupPurgeLineData, self).setUp()
# 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],
}
)
def test_pointing_nowhere(self):
wizard = self.env["cleanup.purge.wizard.data"].create({})
wizard.purge_all()
# must be removed by the wizard
with self.assertRaises(ValueError):
self.env.ref("database_cleanup.test_no_data_entry")

View File

@ -0,0 +1,34 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from .common import Common
class TestCleanupPurgeLineMenu(Common):
def setUp(self):
super(TestCleanupPurgeLineMenu, self).setUp()
# create a new empty menu
self.menu = self.env["ir.ui.menu"].create({"name": "database_cleanup_test"})
def test_empty_menu(self):
wizard = self.env["cleanup.purge.wizard.menu"].create(
{
"purge_line_ids": [
(
0,
0,
{
"menu_id": self.menu.id,
},
)
]
}
)
wizard.purge_all()
self.assertFalse(
self.env["ir.ui.menu"].search(
[
("name", "=", "database_cleanup_test"),
]
)
)

View File

@ -0,0 +1,40 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from .common import Common
class TestCleanupPurgeLineColumn(Common):
def setUp(self):
super(TestCleanupPurgeLineColumn, self).setUp()
# create a nonexistent model
self.model_name = "x_database.cleanup.test.model"
self.model_values = {
"name": "Database cleanup test model",
"model": self.model_name,
}
self.model = self.env["ir.model"].create(self.model_values)
self.env.cr.execute(
"insert into ir_attachment (name, res_model, res_id, type) values "
"('test attachment', %s, 42, 'binary')",
[self.model_name],
)
self.env.registry.models.pop(self.model_name)
def tearDown(self):
""" We recreate the model to avoid registry Exception at loading """
super(TestCleanupPurgeLineColumn, self).tearDown()
# FIXME: issue origin is not clear but it must be addressed.
self.model = self.env["ir.model"].create(self.model_values)
def test_empty_model(self):
wizard = self.env["cleanup.purge.wizard.model"].create({})
wizard.purge_all()
# must be removed by the wizard
self.assertFalse(
self.env["ir.model"].search(
[
("model", "=", self.model_name),
]
)
)

View File

@ -0,0 +1,35 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import threading
from odoo.tools import config
from .common import Common
class TestCleanupPurgeLineModule(Common):
def setUp(self):
super(TestCleanupPurgeLineModule, self).setUp()
# create a nonexistent module
self.module = self.env["ir.module.module"].create(
{
"name": "database_cleanup_test",
"state": "to upgrade",
}
)
def test_remove_to_upgrade_module(self):
wizard = self.env["cleanup.purge.wizard.module"].create({})
config.options["test_enable"] = False # Maybe useless now ?!
self.patch(threading.currentThread(), "testing", False)
wizard.purge_all()
config.options["test_enable"] = True # Maybe useless now ?!
self.patch(threading.currentThread(), "testing", True)
# must be removed by the wizard
self.assertFalse(
self.env["ir.module.module"].search(
[
("name", "=", "database_cleanup_test"),
]
)
)

View File

@ -0,0 +1,39 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from .common import Common
class TestCleanupPurgeLineProperty(Common):
def setUp(self):
super(TestCleanupPurgeLineProperty, self).setUp()
# Create one property for tests
self.partner_name_field_id = self.env["ir.model.fields"].search(
[("name", "=", "name"), ("model_id.model", "=", "res.partner")], limit=1
)
def test_property_to_not_removed(self):
self.property = self.env["ir.property"].create(
{
"fields_id": self.partner_name_field_id.id,
"type": "char",
"value_text": "My default partner name",
"res_id": False,
}
)
wizard = self.env["cleanup.purge.wizard.property"].create({})
wizard.purge_all()
self.assertTrue(self.property.exists())
def test_property_no_value(self):
self.property = self.env["ir.property"].create(
{
"fields_id": self.partner_name_field_id.id,
"type": "char",
"value_text": False,
"res_id": False,
}
)
wizard = self.env["cleanup.purge.wizard.property"].create({})
wizard.purge_all()
self.assertFalse(self.property.exists())

View File

@ -0,0 +1,22 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from psycopg2 import ProgrammingError
from odoo.tools import mute_logger
from .common import Common
class TestCleanupPurgeLineTable(Common):
def setUp(self):
super(TestCleanupPurgeLineTable, self).setUp()
# create an orphaned table
self.env.cr.execute("create table database_cleanup_test (test int)")
def test_empty_table(self):
wizard = self.env["cleanup.purge.wizard.table"].create({})
wizard.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")

View File

@ -15,7 +15,7 @@
<field name="state">code</field>
<field name="model_id" ref="database_cleanup.model_cleanup_purge_wizard_menu" />
<field name="code">
action = env.get('cleanup.purge.wizard.menu').get_wizard_action()
action = env.get('cleanup.purge.wizard').get_wizard_action()
</field>
</record>