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