diff options
| author | Xavier-Do <xdo@odoo.com> | 2019-04-12 13:21:18 +0000 |
|---|---|---|
| committer | Thibault Delavallée <tde@odoo.com> | 2019-05-29 13:34:32 +0000 |
| commit | de7c119e666d108564b3889d1c08e5bf2e4da082 (patch) | |
| tree | 3c24844df7072ba840d93fb1f2b141ad7a507fa3 /addons/mail/models/mail_message.py | |
| parent | 2fcb3c658df3e504b92ff9b5a805d150cfa7effd (diff) | |
[IMP] mail: reduce message_post query count and clean code
Purpose of this commit is to improve performance of post and notify through
various code improvements.
Containing
* making of _notify_customize_recipients a mail.thread method;
* _message_auto_subscribe_followers optimized;
* renaming and optimisation of _notify_partners;
* message_post_attachements optization;
* remove unused mail_post_autofollow_partner_ids context key;
* minimize message create queries;
* only check record related access right for thread messages not in pending
moderation;
* get attachements from values instead of db;
* storing lang in ctx from the beginning;
* don't track on write on attachments;
Related to task 1943901
Linked to PR #32404
Diffstat (limited to 'addons/mail/models/mail_message.py')
| -rw-r--r-- | addons/mail/models/mail_message.py | 127 |
1 files changed, 85 insertions, 42 deletions
diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index 95fc750066b..e1fc850d433 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -393,8 +393,8 @@ class Message(models.Model): customer_email_data.append((partner_tree[notification.res_partner_id.id][0], partner_tree[notification.res_partner_id.id][1], notification.email_status)) has_access_to_model = message.model and self.env[message.model].check_access_rights('read', raise_exception=False) - if message.attachment_ids: - main_attachment = has_access_to_model and message.res_id and self.env[message.model].search([('id', '=', message.res_id)]) and getattr(self.env[message.model].browse(message.res_id), 'message_main_attachment_id') + if message.attachment_ids and message.res_id and issubclass(self.pool[message.model], self.pool['mail.thread']) and has_access_to_model: + main_attachment = self.env[message.model].browse(message.res_id).message_main_attachment_id attachment_ids = [] for attachment in message.attachment_ids: if attachment.id in attachments_tree: @@ -860,6 +860,55 @@ class Message(models.Model): author_ids = [mid for mid, message in message_values.items() if not self.is_thread_message(message)] + # Moderator condition: allow to WRITE, UNLINK if moderator of a pending message + moderator_ids = [] + if operation in ['write', 'unlink']: + moderator_ids = [mid for mid, message in message_values.items() if message.get('moderator_id')] + messages_to_check = self.ids + messages_to_check = set(messages_to_check).difference(set(author_ids), set(moderator_ids)) + if not messages_to_check: + return + + # Recipients condition, for read and write (partner_ids) + # keep on top, usefull for systray notifications + notified_ids = [] + model_record_ids = _generate_model_record_ids(message_values, messages_to_check) + if operation in ['read', 'write']: + notified_ids = [mid for mid, message in message_values.items() if message.get('notified')] + + messages_to_check = set(messages_to_check).difference(set(notified_ids)) + if not messages_to_check: + return + + # CRUD: Access rights related to the document + document_related_ids = [] + document_related_candidate_ids = [mid for mid, message in message_values.items() + if (message.get('model') and message.get('res_id') and + message.get('message_type') != 'user_notification' and + (message.get('moderation_status') != 'pending_moderation' or operation not in ['write', 'unlink']))] + model_record_ids = _generate_model_record_ids(message_values, document_related_candidate_ids) + for model, doc_ids in model_record_ids.items(): + DocumentModel = self.env[model] + if hasattr(DocumentModel, 'get_mail_message_access'): + check_operation = DocumentModel.get_mail_message_access(doc_ids, operation) ## why not giving model here? + else: + check_operation = self.env['mail.thread'].get_mail_message_access(doc_ids, operation, model_name=model) + records = DocumentModel.browse(doc_ids) + records.check_access_rights(check_operation) + mids = records.browse(doc_ids)._filter_access_rules(check_operation) + document_related_ids += [ + mid for mid, message in message_values.items() + if (message.get('model') == model and + message.get('res_id') in mids.ids and + message.get('message_type') != 'user_notification' and + (message.get('moderation_status') != 'pending_moderation' or + operation not in ['write', 'unlink']))] + + messages_to_check = messages_to_check.difference(set(document_related_ids)) + + if not messages_to_check: + return + # Parent condition, for create (check for received notifications for the created message parent) notified_ids = [] if operation == 'create': @@ -880,17 +929,12 @@ class Message(models.Model): notified_ids += [mid for mid, message in message_values.items() if message.get('parent_id') in not_parent_ids] - # Moderator condition: allow to WRITE, UNLINK if moderator of a pending message - moderator_ids = [] - if operation in ['write', 'unlink']: - moderator_ids = [mid for mid, message in message_values.items() if message.get('moderator_id')] + messages_to_check = messages_to_check.difference(set(notified_ids)) + if not messages_to_check: + return - # Recipients condition, for read and write (partner_ids) and create (message_follower_ids) - other_ids = set(self.ids).difference(set(author_ids), set(notified_ids), set(moderator_ids)) - model_record_ids = _generate_model_record_ids(message_values, other_ids) - if operation in ['read', 'write']: - notified_ids = [mid for mid, message in message_values.items() if message.get('notified')] - elif operation == 'create': + # Recipients condition for create (message_follower_ids) + if operation == 'create': for doc_model, doc_ids in model_record_ids.items(): followers = self.env['mail.followers'].sudo().search([ ('res_model', '=', doc_model), @@ -904,28 +948,11 @@ class Message(models.Model): message.get('message_type') != 'user_notification' ] - # CRUD: Access rights related to the document - other_ids = other_ids.difference(set(notified_ids)) - model_record_ids = _generate_model_record_ids(message_values, other_ids) - document_related_ids = [] - for model, doc_ids in model_record_ids.items(): - DocumentModel = self.env[model] - mids = DocumentModel.browse(doc_ids).exists() - if hasattr(DocumentModel, 'check_mail_message_access'): - DocumentModel.check_mail_message_access(mids.ids, operation) # ?? mids ? - else: - self.env['mail.thread'].check_mail_message_access(mids.ids, operation, model_name=model) - document_related_ids += [ - mid for mid, message in message_values.items() - if (message.get('model') == model and - message.get('res_id') in mids.ids and - message.get('message_type') != 'user_notification' and - (message.get('moderation_status') != 'pending_moderation' or - operation not in ['write', 'unlink']))] + messages_to_check = messages_to_check.difference(set(notified_ids)) + if not messages_to_check: + return - # Calculate remaining ids: if not void, raise an error - other_ids = other_ids.difference(set(document_related_ids)) - if not (other_ids and self.browse(other_ids).exists()): + if not self.browse(messages_to_check).exists(): return raise AccessError( _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % @@ -966,17 +993,19 @@ class Message(models.Model): return message_id @api.multi - def _invalidate_documents(self): + def _invalidate_documents(self, model=None, res_id=None): """ Invalidate the cache of the documents followed by ``self``. """ for record in self: - if record.is_thread_message() and 'message_ids' in self.env[record.model]: - self.env[record.model].invalidate_cache(fnames=[ + model = model or record.model + res_id = res_id or record.res_id + if 'message_ids' in self.env[model]: + self.env[model].invalidate_cache(fnames=[ 'message_ids', 'message_unread', 'message_unread_counter', 'message_needaction', 'message_needaction_counter', - ], ids=[record.res_id]) + ], ids=[res_id]) @api.model def create(self, values): @@ -994,8 +1023,7 @@ class Message(models.Model): values['record_name'] = self._get_record_name(values) if 'attachment_ids' not in values: - values.setdefault('attachment_ids', []) - + values['attachment_ids'] = [] # extract base64 images if 'body' in values: Attachments = self.env['ir.attachment'] @@ -1021,8 +1049,21 @@ class Message(models.Model): tracking_values_cmd = values.pop('tracking_value_ids', False) message = super(Message, self).create(values) + # check attachement access if values.get('attachment_ids'): - message.attachment_ids.check(mode='read') + attachment_ids = [] + if all(isinstance(command, int) or command[0] in (4, 6) for command in values.get('attachment_ids')): + attachment_ids = [] + for command in values.get('attachment_ids'): + if isinstance(command, int): + attachment_ids += [command] + elif command[0] == 6: + attachment_ids += command[2] + else: # command[0] == 4: + attachment_ids += [command[1]] + else: + attachment_ids = message.mapped('attachment_ids').ids # fallback on read if any unknow command + self.env['ir.attachment'].browse(attachment_ids).check(mode='read') if tracking_values_cmd: vals_lst = [dict(cmd[2], mail_message_id=message.id) for cmd in tracking_values_cmd if len(cmd) == 3 and cmd[0] == 0] @@ -1033,7 +1074,7 @@ class Message(models.Model): message.sudo().write({'tracking_value_ids': tracking_values_cmd}) if message.is_thread_message(values): - message._invalidate_documents() + message._invalidate_documents(values.get('model'), values.get('res_id')) return message @@ -1067,7 +1108,9 @@ class Message(models.Model): self.mapped('attachment_ids').filtered( lambda attach: attach.res_model == self._name and (attach.res_id in self.ids or attach.res_id == 0) ).unlink() - self._invalidate_documents() + for elem in self: + if elem.is_thread_message(): + elem._invalidate_documents() return super(Message, self).unlink() # -------------------------------------------------- |
