From 1fb20fe3047cf53eff258705d12319015ba96747 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 14 Mar 2019 14:06:24 +0000 Subject: [PATCH] [IMP] base: try to batch update of children address & commercial fields With profiling enabled, importing 10k partners, with all of them having the same parent (though not with all of them having the same is_company setting) lowers sync / post-process time from ~550s to ~330s. Put it in an override to _load_records_create so it's only active for imports (which is the original report & test case), use a context key to avoid going the post-processing work in create. --- odoo/addons/base/models/res_partner.py | 77 +++++++++++++++++++++----- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/odoo/addons/base/models/res_partner.py b/odoo/addons/base/models/res_partner.py index 93d7b175a78..7512252988d 100644 --- a/odoo/addons/base/models/res_partner.py +++ b/odoo/addons/base/models/res_partner.py @@ -2,6 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import base64 +import collections import datetime import hashlib import pytz @@ -469,21 +470,25 @@ class Partner(models.Model): self.update_address(onchange_vals) # 2. To DOWNSTREAM: sync children - if self.child_ids: - # 2a. Commercial Fields: sync if commercial entity - if self.commercial_partner_id == self: - commercial_fields = self._commercial_fields() - if any(field in values for field in commercial_fields): - self._commercial_sync_to_children() - for child in self.child_ids.filtered(lambda c: not c.is_company): - if child.commercial_partner_id != self.commercial_partner_id : - self._commercial_sync_to_children() - break - # 2b. Address fields: sync if address changed - address_fields = self._address_fields() - if any(field in values for field in address_fields): - contacts = self.child_ids.filtered(lambda c: c.type == 'contact') - contacts.update_address(values) + self._children_sync(values) + + def _children_sync(self, values): + if not self.child_ids: + return + # 2a. Commercial Fields: sync if commercial entity + if self.commercial_partner_id == self: + commercial_fields = self._commercial_fields() + if any(field in values for field in commercial_fields): + self._commercial_sync_to_children() + for child in self.child_ids.filtered(lambda c: not c.is_company): + if child.commercial_partner_id != self.commercial_partner_id: + self._commercial_sync_to_children() + break + # 2b. Address fields: sync if address changed + address_fields = self._address_fields() + if any(field in values for field in address_fields): + contacts = self.child_ids.filtered(lambda c: c.type == 'contact') + contacts.update_address(values) @api.multi def _handle_first_contact_creation(self): @@ -556,11 +561,53 @@ class Partner(models.Model): vals['image'] = self._get_default_image(vals.get('type'), vals.get('is_company'), vals.get('parent_id')) tools.image_resize_images(vals, sizes={'image': (1024, None)}) partners = super(Partner, self).create(vals_list) + + if self.env.context.get('_partners_skip_fields_sync'): + return partners + for partner, vals in pycompat.izip(partners, vals_list): partner._fields_sync(vals) partner._handle_first_contact_creation() return partners + def _load_records_create(self, vals_list): + partners = super(Partner, self.with_context(_partners_skip_fields_sync=True))._load_records_create(vals_list) + + # batch up first part of _fields_sync + # group partners by commercial_partner_id (if not self) and parent_id (if type == contact) + groups = collections.defaultdict(list) + for partner, vals in pycompat.izip(partners, vals_list): + cp_id = None + if vals.get('parent_id') and partner.commercial_partner_id != partner: + cp_id = partner.commercial_partner_id.id + + add_id = None + if partner.parent_id and partner.type == 'contact': + add_id = partner.parent_id.id + groups[(cp_id, add_id)].append(partner.id) + + for (cp_id, add_id), children in groups.items(): + # values from parents (commercial, regular) written to their common children + to_write = {} + # commercial fields from commercial partner + if cp_id: + to_write = self.browse(cp_id)._update_fields_values(self._commercial_fields()) + # address fields from parent + if add_id: + parent = self.browse(add_id) + for f in self._address_fields(): + v = parent[f] + if v: + to_write[f] = v.id if isinstance(v, models.BaseModel) else v + if to_write: + self.browse(children).write(to_write) + + # do the second half of _fields_sync the "normal" way + for partner, vals in pycompat.izip(partners, vals_list): + partner._children_sync(vals) + partner._handle_first_contact_creation() + return partners + @api.multi def create_company(self): self.ensure_one()