Files
odoo_source/addons/mail/models/ir_model.py
T
tsm-odoo 1e828a14ec [IMP] mail: get closer from python model definition in test setup
*: hr, hr_holidays, im_livechat, mail, snailmail, test_mail, website_livechat,
website_slides.

This commit will remove the need to duplicate models definition for
test purposes. Indeed, for now, we need to mock the model definitions
on the client side. Those models definitions are often very different
from the ones found on the server.

This approach will allow us :
	- to ease model definitions during tests
	- to get closer from the real models

task-2767820

closes odoo/odoo#84828

Related: odoo/enterprise#24486
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
2022-03-10 18:19:59 +00:00

131 lines
6.1 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, api, fields, models
from odoo.exceptions import UserError
class IrModel(models.Model):
_inherit = 'ir.model'
_order = 'is_mail_thread DESC, name ASC'
is_mail_thread = fields.Boolean(
string="Mail Thread", default=False,
help="Whether this model supports messages and notifications.",
)
is_mail_activity = fields.Boolean(
string="Mail Activity", default=False,
help="Whether this model supports activities.",
)
is_mail_blacklist = fields.Boolean(
string="Mail Blacklist", default=False,
help="Whether this model supports blacklist.",
)
def unlink(self):
# Delete followers, messages and attachments for models that will be unlinked.
models = tuple(self.mapped('model'))
query = "DELETE FROM mail_activity_type WHERE res_model IN %s"
self.env.cr.execute(query, [models])
query = "DELETE FROM mail_followers WHERE res_model IN %s"
self.env.cr.execute(query, [models])
query = "DELETE FROM mail_message WHERE model in %s"
self.env.cr.execute(query, [models])
# Get files attached solely by the models
query = """
SELECT DISTINCT store_fname
FROM ir_attachment
WHERE res_model IN %s
EXCEPT
SELECT store_fname
FROM ir_attachment
WHERE res_model not IN %s;
"""
self.env.cr.execute(query, [models, models])
fnames = self.env.cr.fetchall()
query = """DELETE FROM ir_attachment WHERE res_model in %s"""
self.env.cr.execute(query, [models])
for (fname,) in fnames:
self.env['ir.attachment']._file_delete(fname)
return super(IrModel, self).unlink()
def write(self, vals):
if self and ('is_mail_thread' in vals or 'is_mail_activity' in vals or 'is_mail_blacklist' in vals):
if any(rec.state != 'manual' for rec in self):
raise UserError(_('Only custom models can be modified.'))
if 'is_mail_thread' in vals and any(rec.is_mail_thread > vals['is_mail_thread'] for rec in self):
raise UserError(_('Field "Mail Thread" cannot be changed to "False".'))
if 'is_mail_activity' in vals and any(rec.is_mail_activity > vals['is_mail_activity'] for rec in self):
raise UserError(_('Field "Mail Activity" cannot be changed to "False".'))
if 'is_mail_blacklist' in vals and any(rec.is_mail_blacklist > vals['is_mail_blacklist'] for rec in self):
raise UserError(_('Field "Mail Blacklist" cannot be changed to "False".'))
res = super(IrModel, self).write(vals)
self.flush()
# setup models; this reloads custom models in registry
self.pool.setup_models(self._cr)
# update database schema of models
models = self.pool.descendants(self.mapped('model'), '_inherits')
self.pool.init_models(self._cr, models, dict(self._context, update_custom_fields=True))
else:
res = super(IrModel, self).write(vals)
return res
def _reflect_model_params(self, model):
vals = super(IrModel, self)._reflect_model_params(model)
vals['is_mail_thread'] = issubclass(type(model), self.pool['mail.thread'])
vals['is_mail_activity'] = issubclass(type(model), self.pool['mail.activity.mixin'])
vals['is_mail_blacklist'] = issubclass(type(model), self.pool['mail.thread.blacklist'])
return vals
@api.model
def _instanciate(self, model_data):
model_class = super(IrModel, self)._instanciate(model_data)
if model_data.get('is_mail_thread') and model_class._name != 'mail.thread':
parents = model_class._inherit or []
parents = [parents] if isinstance(parents, str) else parents
model_class._inherit = parents + ['mail.thread']
if model_data.get('is_mail_activity') and model_class._name != 'mail.activity.mixin':
parents = model_class._inherit or []
parents = [parents] if isinstance(parents, str) else parents
model_class._inherit = parents + ['mail.activity.mixin']
if model_data.get('is_mail_blacklist') and model_class._name != 'mail.thread.blacklist':
parents = model_class._inherit or []
parents = [parents] if isinstance(parents, str) else parents
model_class._inherit = parents + ['mail.thread.blacklist']
return model_class
def _get_model_definitions(self, model_names_to_fetch):
fields_by_model_names = {}
for model_name in model_names_to_fetch:
model = self.env[model_name]
# get fields, relational fields are kept only if the related model is in model_names_to_fetch
fields_by_fname = {
fname: field
for fname, field in model.fields_get(
attributes=['name', 'type', 'relation', 'required', 'readonly', 'selection', 'string']
).items()
if not field.get('relation') or field['relation'] in model_names_to_fetch
}
# exclude date/datetime and binary default values: dates will always be wrong since they are dynamic
# and binary values are not needed for now
default_values_by_fname = model.default_get([
fname for fname, field in fields_by_fname.items()
if field['type'] not in ['binary', 'date', 'datetime']
])
tracked_field_names = model._track_get_fields() if 'mail.thread' in model._inherit else []
for fname in tracked_field_names:
if fname in fields_by_fname:
fields_by_fname[fname]['tracking'] = True
for fname, value in default_values_by_fname.items():
if fname in fields_by_fname:
fields_by_fname[fname]['default'] = value
fields_by_model_names[model_name] = fields_by_fname
return fields_by_model_names