odoo/ext/custom-addons/mailchimp/models/ir_model.py

97 lines
4.6 KiB
Python

import logging
from odoo import api, fields, models, SUPERUSER_ID, tools, _
from odoo.exceptions import AccessError, UserError, ValidationError
_logger = logging.getLogger(__name__)
MODULE_UNINSTALL_FLAG = '_force_unlink'
class IrModelData(models.Model):
_inherit = 'ir.model.data'
@api.model
def _module_data_uninstall(self, modules_to_remove):
"""Deletes all the records referenced by the ir.model.data entries
``ids`` along with their corresponding database backed (including
dropping tables, columns, FKs, etc, as long as there is no other
ir.model.data entry holding a reference to them (which indicates that
they are still owned by another module).
Attempts to perform the deletion in an appropriate order to maximize
the chance of gracefully deleting all records.
This step is performed as part of the full uninstallation of a module.
"""
if not (self._uid == SUPERUSER_ID or self.env.user.has_group('base.group_system')):
raise AccessError(_('Administrator access is required to uninstall a module'))
# enable model/field deletion
self = self.with_context(**{MODULE_UNINSTALL_FLAG: True})
datas = self.search([('module', 'in', modules_to_remove)])
to_unlink = tools.OrderedSet()
undeletable = self.browse([])
for data in datas.sorted(key='id', reverse=True):
model = data.model
res_id = data.res_id
if data.model == 'ir.model' and 'mailchimp' in modules_to_remove:
model_id = self.env[model].browse(res_id).with_context(prefetch_fields=False)
if model_id.model == 'mail.mass_mailing.list_contact_rel':
continue
if data.model == 'ir.model.fields' and 'mailchimp' in modules_to_remove:
field_id = self.env[model].browse(res_id).with_context(prefetch_fields=False)
if field_id.name in ['contact_id', 'list_id']:
continue
to_unlink.add((model, res_id))
def unlink_if_refcount(to_unlink):
undeletable = self.browse()
for model, res_id in to_unlink:
external_ids = self.search([('model', '=', model), ('res_id', '=', res_id)])
if external_ids - datas:
# if other modules have defined this record, we must not delete it
continue
if model == 'ir.model.fields':
# Don't remove the LOG_ACCESS_COLUMNS unless _log_access
# has been turned off on the model.
field = self.env[model].browse(res_id).with_context(
prefetch_fields=False,
)
if not field.exists():
_logger.info('Deleting orphan external_ids %s', external_ids)
external_ids.unlink()
continue
if field.name in models.LOG_ACCESS_COLUMNS and field.model in self.env and self.env[field.model]._log_access:
continue
if field.name == 'id':
continue
_logger.info('Deleting %s@%s', res_id, model)
try:
self._cr.execute('SAVEPOINT record_unlink_save')
self.env[model].browse(res_id).unlink()
except Exception:
_logger.info('Unable to delete %s@%s', res_id, model, exc_info=True)
undeletable += external_ids
self._cr.execute('ROLLBACK TO SAVEPOINT record_unlink_save')
else:
self._cr.execute('RELEASE SAVEPOINT record_unlink_save')
return undeletable
# Remove non-model records first, then model fields, and finish with models
undeletable += unlink_if_refcount(item for item in to_unlink if item[0] not in ('ir.model', 'ir.model.fields', 'ir.model.constraint'))
undeletable += unlink_if_refcount(item for item in to_unlink if item[0] == 'ir.model.constraint')
modules = self.env['ir.module.module'].search([('name', 'in', modules_to_remove)])
constraints = self.env['ir.model.constraint'].search([('module', 'in', modules.ids)])
constraints._module_data_uninstall()
undeletable += unlink_if_refcount(item for item in to_unlink if item[0] == 'ir.model.fields')
relations = self.env['ir.model.relation'].search([('module', 'in', modules.ids)])
relations._module_data_uninstall()
undeletable += unlink_if_refcount(item for item in to_unlink if item[0] == 'ir.model')
(datas - undeletable).unlink()