Reputation: 33
I have tried, under Plone 4.3.3, to customize a class method of an archetype content type in one of my products.
I have a product bsw.produit_1
with a content type MyContent
defined as follows:
class MyContent(base.ATCTContent):
implements(IMyContent)
meta_type = "MyContent"
schema = MyContent`
def ma_fonction(self):
......
return res
I want to modify the code of my function ma_fonction
in another product. I have tried using an adapter and following the plone docs, but without success.
The class where I wish to customize the function:
class CustomClass(object):
""" """
implements(IMyContent)
adapts(IMyContent)
def at_post_payment_script(self, obj_transaction):
""" """
......
# My new code
return res
The configure.zcml
where I declared my adapter:
<adapter for="bsw.produit_1.content.mycontent.MyContent"
provides="bsw.produit_1.interfaces.IMyContent"
factory=".customclass.CustomClass" />
In my zcml declaration, I've also tried putting archetypes.schemaextender.interfaces.ISchemaExtender
as provides
or putting the interface IMyContent
for for
instead of the class.
None of these worked, every time, the customized code is not executed. Does anybody have a solution for this?
Upvotes: 3
Views: 362
Reputation: 7819
The simplest method you can use (although not politically correct!) is monkey-patching.
Take a look at collective.monkeypatcher, you simply need a configuration like that (in your 3rd party product):
<monkey:patch
description=""
class="your.package.MyContent"
original="ma_fonction"
replacement=".monkeys.new_ma_fonction"
/>
Then in your package create also a monkeys.py
module with the new method inside:
def new_ma_fonction(self):
# do stuff
return res
Upvotes: 1
Reputation: 6839
The solution you need depends on what you want to achieve.
But archetypes.schemaextender is the wrong solution. schemaextender is there to modify the schema, this includes:
To implement your own adaptera is definitely the right approach.
First you need to implement a adapter for the default behavior.
Second, you need to adapt the context and the request. The request is important, since that's a way to define a more specific adapter if your other
product is installed.
Python code for the default implementation (adapter.py):
from zope.component import adapts
from zope.interface import Interface
from zope.interface import implements
class IBehavior(Interface):
def __init__(context, request)
"""Adapts context and request"""
# ... more ...
class DefaultBehavior(object):
implements(IBehavior)
adapts(IMyContent, Interface) # IMPORTAN two discriminators
def __init__(self, context, request):
self.context = context
self.request = request
def __call__(self):
# your default implementation goes here.
Register the adapter with zcml:
<adapter factory=".adapter.DefaultBehavior" />
Your now able to call the default adapter in ma_fonction
from zope.component import getMultiAdapter
class MyContent(base.ATCTContent)
def ma_fonction(self):
adapter = getMultiAdapter((self, self.REQUEST), IDefaultBehavior)
return adapter()
Now you can implement a more specific adapter in your other
product using a browserlayer. Check documentation, how to register a browserlayer
In your other
package you can now register a adapter which implements the same IBehavior
interface, but also adapts your browserlayer.
from other.package.interfaces import IOtherPackageLayer
from zope.component import adapts
from zope.interface import implements
class DifferenBehavior(object):
implements(IBehavior)
adapts(IMyContent, IOtherPackageLayer) # IMPORTAN adapt the browserlayer not Interface
def __init__(self, context, request):
self.context = context
self.request = request
def __call__(self):
# your different implementation goes here.
Register also with zcml:
<adapter factory=".adapter.DifferenBehavior" />
Your ma_fonction
now calls the default adapter, if the other
package is not installed. And the different adapter if the other
package is installed.
Upvotes: 5