Skip to content
Snippets Groups Projects
Commit 36141931 authored by Raphael Collet's avatar Raphael Collet Committed by GitHub
Browse files

[FIX] mail: remove field res_model_id for performance reasons (#19744)

parent 379f949b
No related branches found
No related tags found
No related merge requests found
......@@ -46,6 +46,12 @@ class IrModel(models.Model):
model_class._inherit = parents + ['mail.thread']
return model_class
def unlink(self):
# Delete followers for models that will be unlinked.
query = "DELETE FROM mail_followers WHERE res_model IN %s"
self.env.cr.execute(query, [tuple(self.mapped('model'))])
return super(IrModel, self).unlink()
class IrModelField(models.Model):
_inherit = 'ir.model.fields'
......
......@@ -18,12 +18,11 @@ class Followers(models.Model):
_log_access = False
_description = 'Document Followers'
res_model_id = fields.Many2one(
'ir.model', 'Related Document Model',
index=True, required=True, ondelete='cascade',
help='Model of the followed resource')
# Note. There is no integrity check on model names for performance reasons.
# However, followers of unlinked models are deleted by models themselves
# (see 'ir.model' inheritance).
res_model = fields.Char(
'Related Document Model Name', index=True, readonly=True, related='res_model_id.model', compute_sudo=True, store=True)
'Related Document Model Name', required=True, index=True)
res_id = fields.Integer(
'Related Document ID', index=True, help='Id of the followed resource')
partner_id = fields.Many2one(
......@@ -40,7 +39,6 @@ class Followers(models.Model):
:param force: if True, delete existing followers before creating new one
using the subtypes given in the parameters
"""
res_model_id = self.env['ir.model']._get(res_model).id
force_mode = force or (all(partner_data.values()) and all(channel_data.values()))
generic = []
specific = {}
......@@ -50,7 +48,7 @@ class Followers(models.Model):
followers = self.sudo().search([
'&',
'&', ('res_model_id', '=', res_model_id), ('res_id', 'in', res_ids),
'&', ('res_model', '=', res_model), ('res_id', 'in', res_ids),
'|', ('partner_id', 'in', list(partner_data)), ('channel_id', 'in', list(channel_data))])
if force_mode:
......@@ -82,9 +80,9 @@ class Followers(models.Model):
gen_new_pids = [pid for pid in partner_data if pid not in p_exist]
gen_new_cids = [cid for cid in channel_data if cid not in c_exist]
for pid in gen_new_pids:
generic.append([0, 0, {'res_model_id': res_model_id, 'partner_id': pid, 'subtype_ids': [(6, 0, partner_data.get(pid) or default_subtypes.ids)]}])
generic.append([0, 0, {'res_model': res_model, 'partner_id': pid, 'subtype_ids': [(6, 0, partner_data.get(pid) or default_subtypes.ids)]}])
for cid in gen_new_cids:
generic.append([0, 0, {'res_model_id': res_model_id, 'channel_id': cid, 'subtype_ids': [(6, 0, channel_data.get(cid) or default_subtypes.ids)]}])
generic.append([0, 0, {'res_model': res_model, 'channel_id': cid, 'subtype_ids': [(6, 0, channel_data.get(cid) or default_subtypes.ids)]}])
# create new followers, each document at a time because of existing followers to avoid erasing
if not force_mode:
......@@ -98,13 +96,13 @@ class Followers(models.Model):
# subscribe new followers
for new_pid in new_pids:
command.append((0, 0, {
'res_model_id': res_model_id,
'res_model': res_model,
'partner_id': new_pid,
'subtype_ids': [(6, 0, partner_data.get(new_pid) or default_subtypes.ids)],
}))
for new_cid in new_cids:
command.append((0, 0, {
'res_model_id': res_model_id,
'res_model': res_model,
'channel_id': new_cid,
'subtype_ids': [(6, 0, channel_data.get(new_cid) or default_subtypes.ids)],
}))
......
......@@ -26,13 +26,12 @@ class TestMailFollowers(TestMail):
'mail.test', groups.ids,
{self.user_employee.partner_id.id: [self.mt_mg_nodef.id]},
{test_channel.id: [self.mt_al_nodef.id]})
mail_channel_model_id = self.env['ir.model']._get('mail.test').id
self.assertFalse(specific)
self.assertEqual(len(generic), 2)
items = [it[2] for it in generic]
self.assertEqual({item['res_model_id'] for item in items},
{mail_channel_model_id})
self.assertEqual({item['res_model'] for item in items},
{'mail.test'})
self.assertEqual({item['channel_id'] for item in items if item.get('channel_id')},
{test_channel.id})
self.assertEqual({item['partner_id'] for item in items if item.get('partner_id')},
......@@ -42,9 +41,8 @@ class TestMailFollowers(TestMail):
def test_m2o_command_update_selective(self):
test_channel = self.env['mail.channel'].create({'name': 'Test'})
mail_channel_model_id = self.env['ir.model']._get('mail.test').id
groups = self.test_pigs | self.test_public
self.env['mail.followers'].create({'partner_id': self.user_employee.partner_id.id, 'res_model_id': mail_channel_model_id, 'res_id': self.test_pigs.id})
self.env['mail.followers'].create({'partner_id': self.user_employee.partner_id.id, 'res_model': 'mail.test', 'res_id': self.test_pigs.id})
generic, specific = self.env['mail.followers']._add_follower_command(
'mail.test', groups.ids,
{self.user_employee.partner_id.id: [self.mt_mg_nodef.id]},
......@@ -53,12 +51,12 @@ class TestMailFollowers(TestMail):
self.assertEqual(len(generic), 1)
self.assertEqual(len(specific), 1)
self.assertEqual(generic[0][2]['res_model_id'], mail_channel_model_id)
self.assertEqual(generic[0][2]['res_model'], 'mail.test')
self.assertEqual(generic[0][2]['channel_id'], test_channel.id)
self.assertEqual(set(generic[0][2]['subtype_ids'][0][2]), set(self.default_group_subtypes.ids))
self.assertEqual(list(specific), [self.test_public.id])
self.assertEqual(specific[self.test_public.id][0][2]['res_model_id'], mail_channel_model_id)
self.assertEqual(specific[self.test_public.id][0][2]['res_model'], 'mail.test')
self.assertEqual(specific[self.test_public.id][0][2]['partner_id'], self.user_employee.partner_id.id)
self.assertEqual(set(specific[self.test_public.id][0][2]['subtype_ids'][0][2]), set([self.mt_mg_nodef.id]))
......@@ -139,7 +137,7 @@ class TestMailFollowers(TestMail):
test_channel = self.env['mail.channel'].create({'name': 'Follower Channel'})
with self.assertRaises(IntegrityError), mute_logger('odoo.sql_db'):
self.env['mail.followers'].create({
'res_model_id': self.env['ir.model']._get('mail.test').id,
'res_model': test_record._name,
'res_id': test_record.id,
'partner_id': self.user_employee.partner_id.id,
'channel_id': test_channel.id,
......
......@@ -9,7 +9,7 @@
<field name="priority">10</field>
<field name="arch" type="xml">
<tree string="Followers">
<field name="res_model_id"/>
<field name="res_model"/>
<field name="res_id"/>
<field name="partner_id"/>
</tree>
......@@ -24,7 +24,7 @@
<sheet>
<group>
<group>
<field name="res_model_id"/>
<field name="res_model"/>
<field name="partner_id"/>
</group>
<group>
......
......@@ -180,7 +180,7 @@ class TestPerformance(TransactionCase):
model = self.env['test_performance.mail']
model.with_context(tracking_disable=True).create({'name': self.str('X')})
@queryCount(admin=39, demo=55)
@queryCount(admin=38, demo=54)
def test_create_mail_with_tracking(self):
""" Create records inheriting from 'mail.thread' (with field tracking). """
model = self.env['test_performance.mail']
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment