You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

92 lines
3.6 KiB

  1. # -*- coding: utf-8 -*-
  2. # © 2016 Therp BV <http://therp.nl>
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  4. import logging
  5. from openerp import SUPERUSER_ID, models
  6. _logger = logging.getLogger(__file__)
  7. # TODO: this should be a helper module to cetralize the point of failure
  8. # in case this introduces tacit bugs
  9. class BasePatchModelsMixin(models.AbstractModel):
  10. """
  11. This is a mixin class meant to simplify working with patches
  12. on BaseModel or on abstract models like mail.thread.
  13. If you just change them, models created earlier will lack the
  14. attributes you add. Just inherit from this mixin, it will check
  15. which existing models need your changes and apply them.
  16. In your module, do something like
  17. class MailThread(models.AbstractModel):
  18. _name = 'mail.thread'
  19. _inherit = ['base.patch.models.mixin', 'mail.thread']
  20. in case you need to patch BaseModel, say
  21. class BaseModel(models.BaseModel):
  22. _name = 'my.unique.model.name'
  23. _inherit = 'base.patch.models.mixin'
  24. Your code will behave as if it was an inherited class of the class you pass
  25. in the second parameter to _base_patch_models.
  26. """
  27. _name = 'base.patch.models.mixin'
  28. def _base_patch_models(self, cr, our_class=None, parent_class=None):
  29. """iterate through existing models to apply our changes there if
  30. necessary"""
  31. if self._name == BasePatchModelsMixin._name:
  32. return
  33. my_bases = self.__class__.__bases__
  34. for i in range(max(len(my_bases) - 1, 0)):
  35. if my_bases[i]._name == BasePatchModelsMixin._name:
  36. our_class = my_bases[i - 1]
  37. parent_class = my_bases[i + 1]
  38. inherit = [self._inherit]\
  39. if isinstance(self._inherit, basestring) else self._inherit
  40. for i in range(len(my_bases) - 1, 0, -1):
  41. # this can be different from the above if our mixin is used
  42. # multiple times on the same model
  43. if my_bases[i]._name in inherit:
  44. parent_class = my_bases[i]
  45. break
  46. if self.__class__.__bases__[-1] == BasePatchModelsMixin\
  47. and not our_class or not parent_class:
  48. our_class = self.__class__.__bases__[-2]
  49. parent_class = models.BaseModel
  50. if not our_class or not parent_class:
  51. _logger.info(
  52. 'Failed to autodetect own class or parent class for %s, '
  53. 'ignoring', self._name)
  54. return
  55. for model_name, model_object in self.pool.models.iteritems():
  56. if not isinstance(model_object, parent_class):
  57. continue
  58. if isinstance(model_object, our_class):
  59. continue
  60. if not isinstance(model_object, models.Model):
  61. continue
  62. bases = list(model_object.__class__.__bases__)
  63. position = 1
  64. if parent_class == models.BaseModel:
  65. position = len(bases)
  66. else:
  67. for i in range(len(bases) - 1, position, -1):
  68. if bases[i]._name in inherit:
  69. position = i
  70. break
  71. bases.insert(position, our_class)
  72. model_object.__class__.__bases__ = tuple(bases)
  73. def _register_hook(self, cr):
  74. self._base_patch_models(cr)
  75. for base in self.__class__.__bases__:
  76. if not hasattr(super(base, self), '_register_hook'):
  77. return
  78. if super(base, self)._register_hook != self._register_hook:
  79. return super(base, self)._register_hook.__func__(
  80. super(base, self), cr)