From 83e0ef2b8258afcd448faee23f33e6b399196cfe Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Thu, 28 Jan 2016 16:42:24 +0100 Subject: [PATCH] [IMP] use base_patch_models_mixin --- .../models/__init__.py | 1 + .../models/base_patch_models_mixin.py | 92 +++++++++++++++++++ .../models/mail_thread.py | 36 +------- 3 files changed, 97 insertions(+), 32 deletions(-) create mode 100644 mail_follower_custom_notification/models/base_patch_models_mixin.py diff --git a/mail_follower_custom_notification/models/__init__.py b/mail_follower_custom_notification/models/__init__.py index b0a8e2ce..507fec24 100644 --- a/mail_follower_custom_notification/models/__init__.py +++ b/mail_follower_custom_notification/models/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # © 2015 Therp BV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import base_patch_models_mixin from . import mail_followers from . import mail_thread from . import mail_message diff --git a/mail_follower_custom_notification/models/base_patch_models_mixin.py b/mail_follower_custom_notification/models/base_patch_models_mixin.py new file mode 100644 index 00000000..0107f4fc --- /dev/null +++ b/mail_follower_custom_notification/models/base_patch_models_mixin.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- +# © 2016 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import logging +from openerp import SUPERUSER_ID, models + + +_logger = logging.getLogger(__file__) + + +# TODO: this should be a helper module to cetralize the point of failure +# in case this introduces tacit bugs +class BasePatchModelsMixin(models.AbstractModel): + """ + This is a mixin class meant to simplify working with patches + on BaseModel or on abstract models like mail.thread. + If you just change them, models created earlier will lack the + attributes you add. Just inherit from this mixin, it will check + which existing models need your changes and apply them. + + In your module, do something like + + class MailThread(models.AbstractModel): + _name = 'mail.thread' + _inherit = ['base.patch.models.mixin', 'mail.thread'] + + in case you need to patch BaseModel, say + + class BaseModel(models.BaseModel): + _name = 'my.unique.model.name' + _inherit = 'base.patch.models.mixin' + + Your code will behave as if it was an inherited class of the class you pass + in the second parameter to _base_patch_models. + """ + _name = 'base.patch.models.mixin' + + def _base_patch_models(self, cr, our_class=None, parent_class=None): + """iterate through existing models to apply our changes there if + necessary""" + if self._name == BasePatchModelsMixin._name: + return + my_bases = self.__class__.__bases__ + for i in range(max(len(my_bases) - 1, 0)): + if my_bases[i]._name == BasePatchModelsMixin._name: + our_class = my_bases[i - 1] + parent_class = my_bases[i + 1] + inherit = [self._inherit]\ + if isinstance(self._inherit, basestring) else self._inherit + for i in range(len(my_bases) - 1, 0, -1): + # this can be different from the above if our mixin is used + # multiple times on the same model + if my_bases[i]._name in inherit: + parent_class = my_bases[i] + break + if self.__class__.__bases__[-1] == BasePatchModelsMixin\ + and not our_class or not parent_class: + our_class = self.__class__.__bases__[-2] + parent_class = models.BaseModel + if not our_class or not parent_class: + _logger.info( + 'Failed to autodetect own class or parent class for %s, ' + 'ignoring', self._name) + return + + for model_name, model_object in self.pool.models.iteritems(): + if not isinstance(model_object, parent_class): + continue + if isinstance(model_object, our_class): + continue + if not isinstance(model_object, models.Model): + continue + bases = list(model_object.__class__.__bases__) + position = 1 + if parent_class == models.BaseModel: + position = len(bases) + else: + for i in range(len(bases) - 1, position, -1): + if bases[i]._name in inherit: + position = i + break + bases.insert(position, our_class) + model_object.__class__.__bases__ = tuple(bases) + + def _register_hook(self, cr): + self._base_patch_models(cr) + for base in self.__class__.__bases__: + if not hasattr(super(base, self), '_register_hook'): + return + if super(base, self)._register_hook != self._register_hook: + return super(base, self)._register_hook.__func__( + super(base, self), cr) diff --git a/mail_follower_custom_notification/models/mail_thread.py b/mail_follower_custom_notification/models/mail_thread.py index 910eb222..11c23eb8 100644 --- a/mail_follower_custom_notification/models/mail_thread.py +++ b/mail_follower_custom_notification/models/mail_thread.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- # © 2015 Therp BV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import SUPERUSER_ID, api, models -from openerp.addons.mail.mail_thread import mail_thread +from openerp import api, models -class MailThread(models.Model): - _inherit = 'mail.thread' +class MailThread(models.AbstractModel): + _inherit = ['base.patch.models.mixin', 'mail.thread'] + _name = 'mail.thread' @api.multi def _get_subscription_data(self, name, args, user_pid=None): @@ -78,31 +78,3 @@ class MailThread(models.Model): 'force_own_subtype_ids': [(6, 0, ids_with_value( data, 'force_own', '1'))] }), - - def _register_hook(self, cr): - model_ids = self.pool['ir.model'].search(cr, SUPERUSER_ID, []) - rebuilt = [] - for model in self.pool['ir.model'].browse(cr, SUPERUSER_ID, model_ids): - if model.model not in self.pool: - continue - model_object = self.pool[model.model] - if not isinstance(model_object, mail_thread): - continue - if isinstance(model_object, MailThread): - continue - bases = list(model_object.__class__.__bases__) - if MailThread not in bases: - bases.insert(1, MailThread) - class_dict = dict(model_object.__dict__) - class_dict['_inherit'] = model_object._name - new_model_class = type(model_object._name, tuple(bases), - class_dict) - new_model = new_model_class._build_model(self.pool, cr) - self.pool.models[model.model] = new_model - new_model._prepare_setup(cr, SUPERUSER_ID) - new_model._setup_base(cr, SUPERUSER_ID, False) - new_model._setup_fields(cr, SUPERUSER_ID) - rebuilt.append(new_model) - for model in rebuilt: - model._setup_complete(cr, SUPERUSER_ID) - return super(MailThread, self)._register_hook(cr)