# -*- coding: utf-8 -*- # © 2016 Therp BV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging from openerp import 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)