From b6b517ee4c415e6e4dbad3feb822ee7f60fc9217 Mon Sep 17 00:00:00 2001 From: Xiaowu Zhang <xiaowu.zhang@nexedi.com> Date: Wed, 4 Sep 2019 11:01:11 +0200 Subject: [PATCH] portal_type_class: possible to overwrite methods or properties in mixin Before this change, class hierarchy is like this when using mixin: ``` class MyClass(BaseClass, Mixin1....) ``` which is usually ok when classes don't override each other's But if we want to overwrite BaseClass's method by using mixin to do more thing, For example: ``` class BaseClass(object): def test(self): print 'base test' class Mixin1(object): def test(self): super(Mixin,self).test() print 'mixin' ``` I want to display 'mixin base test' when call test, but it doesn't work since priority of how methods are resolved is from left to right: BaseClass----->Mixin1, it only display 'base test' So the correct way to use mixin should be in reverse order: ``` class MyClass(Mixin1, BaseClass) ``` /reviewed-on https://lab.nexedi.com/nexedi/erp5/merge_requests/935 --- product/ERP5Type/dynamic/portal_type_class.py | 2 +- .../ERP5Type/tests/testDynamicClassGeneration.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/product/ERP5Type/dynamic/portal_type_class.py b/product/ERP5Type/dynamic/portal_type_class.py index bac7a13d0b..7736c4c81b 100644 --- a/product/ERP5Type/dynamic/portal_type_class.py +++ b/product/ERP5Type/dynamic/portal_type_class.py @@ -285,7 +285,7 @@ def generatePortalTypeClass(site, portal_type_name): mixin_class_list.append(mixin_class) - base_class_list = [klass] + accessor_holder_list + mixin_class_list + [ + base_class_list = mixin_class_list + [klass] + accessor_holder_list + [ # _getAcquireLocalRoles is accessed by security machinery, so it needs to # be fast: make it a ConstantGetter while we have access to portal_type # configuration. diff --git a/product/ERP5Type/tests/testDynamicClassGeneration.py b/product/ERP5Type/tests/testDynamicClassGeneration.py index 1490d43709..c6e627e0dc 100644 --- a/product/ERP5Type/tests/testDynamicClassGeneration.py +++ b/product/ERP5Type/tests/testDynamicClassGeneration.py @@ -2559,15 +2559,18 @@ class TestZodbMixinComponent(TestZodbInterfaceComponent): """ Return 42 """ + def getTitle(self, **kw): + return "Test Mixin" ''' % class_name def testAssignToPortalTypeClass(self): """ Create a new Document Component inheriting from Person Document and try to - assign it to Person Portal Type, then create a new Person and check - whether it has been successfully added to its Portal Type class bases and - that the newly-defined function on ZODB Component can be called as well as - methods from Person Document + assign it to Person Portal Type, in this Component, define getTitle method, + then create a new Person and check whether it has been successfully added + to its Portal Type class bases and that the newly-defined function + on ZODB Component can be called as well as methods from Person Document,also + check getTitle method overwrite the original one """ import erp5.portal_type person_type = self.portal.portal_types.Person @@ -2588,6 +2591,9 @@ class TestZodbMixinComponent(TestZodbInterfaceComponent): person_type_class_mro_list = person_type_class.__mro__ self.assertFalse(TestPortalTypeMixin in person_type_class_mro_list) person_original_mixin_type_list = list(person_type.getTypeMixinList()) + person_module = self.portal.person_module + person = person_module.newContent(id='Mixin', portal_type='Person') + original_title = person.getTitle() try: person_type.setTypeMixinList(person_original_mixin_type_list + ['TestPortalTypeMixin']) @@ -2598,6 +2604,8 @@ class TestZodbMixinComponent(TestZodbInterfaceComponent): person_type_class_mro_list = person_type_class.__mro__ from erp5.component.mixin.TestPortalTypeMixin import TestPortalTypeMixin self.assertTrue(TestPortalTypeMixin in person_type_class_mro_list) + self.assertEqual('Test Mixin', person.getTitle()) + self.assertNotEqual(original_title, person.getTitle()) finally: person_type.setTypeMixinList(person_original_mixin_type_list) -- 2.30.9