3

I've been using a schema behavior with no problem, but I want to also have a method that provides some logic. Right now I have

class IMyFoo(form.Schema):
    requester = schema.TextLine(title=_(u"Requestor"),
              required=False,
          )

    def foo(self):
      """ foo """

alsoProvides(IMyFoo, IFormFieldProvider)

And in the zcml

<plone:behavior
    title="My behavior"
    description="Some desc"
    provides=".behaviors.IMyFoo"
    for=".interfaces.ISomeInterface"
    />

I included IMyFoo in the behaviors section of a content type in portal_types. This gives me the schema but not that foo() method. So I tried to add a factory for it from reading http://plone-training.readthedocs.org/en/latest/behaviors2.html with the following code

class MyFoo(object):    
    def foo(self):
      return 'bar'

And in the zcml

<plone:behavior
    title="My behavior"
    description="Some desc"
    provides=".behaviors.IMyFoo"
    factory=".behaviors.MyFoo"
    for=".interfaces.ISomeInterface"
    />

But this didn't seem to make a difference, or at least, I don't know how to access that method. The closest I've been able to come is the following:

class IMyFoo(Interface):
  """ Marker """

class MyFoo(object):

  def __init__(self, context):
      self.context = context

  def foo(self):
    return 'bar'

<adapter for=".interfaces.ISomeInterface"
     factory=".behaviors.MyFoo"
     provides=".behaviors.IMyFoo" />

I put IMyFoo in the behaviors attribute in the fti, and then call it by walking through all behaviors with something like

behavior = resolveDottedName(context.portal_types.getTypeInfo(context.portal_type).behaviors[-1]))
behavior(self).myfoo()

Surely going through the FTI like that is not the proper way to do it. But I'm at a loss at this point. In Archetypes I would just make a mixin class and inherit it with any content type I wanted to use it. I could do the same here, but my understanding is that behaviors are supposed to be a replacement for them, so I'd like to figure out how to use this preferred method.

Esoth
  • 437
  • 2
  • 9
  • Maybe I should just use browser views? I don't really need to do anything with the request object so that seems a bit silly, but it would work. – Esoth Jan 30 '15 at 17:32

1 Answers1

6

As you've discovered, the schema class really is just an interface. It can't provide any methods. To provide more functionality, you need to connect your behavior interface to a factory class that adapts a dexterity object to provide your interface.

So, if your behaviors.py looks like this:

# your imports plus:
from plone.dexterity.interfaces import IDexterityContent
from zope.component import adapts
from zope.interface import implements

class IMyFoo(form.Schema):
    requester = schema.TextLine(
      title=_(u"Requestor"),
      required=False,
      )

    def foo(self):
      """ foo """

alsoProvides(IMyFoo, IFormFieldProvider)

class MyFoo(object):    
    implements(IMyFoo)
    adapts(IDexterityContent)

    def __init__(self, context):
        self.context = context

    def foo(self):
      return 'bar'

Then your one-and-only zcml declaration would be:

<plone:behavior
    title="My behavior name"
    description="Behavior description"
    provides=".behavior.IMyFoo"
    factory=".behavior.MyFoo"
    for="plone.dexterity.interfaces.IDexterityContent"
    />

And, you would reach your method with code like:

IMyFoo(myFooishObject).foo()

Note the use of IDexterityContent. You're creating a behavior that could be applied to any Dexterity content. So, the behavior adapter should be for that very general interface.

SteveM
  • 6,058
  • 1
  • 16
  • 20
  • Thanks Steve. This still requires that I know I want to use the IMyFoo interface. If I want to find all behaviors that provide a foo method, I'd have to iterate through the list of behaviors defined in the fti? – Esoth Feb 03 '15 at 15:28
  • And, what if some behavior had a foo method that did something you didn't want? The whole point of the component architecture is to isolate the aspects of an object. Last tenant of the Zen of Python: "Namespaces are one honking great idea -- let's do more of those!" – SteveM Feb 03 '15 at 16:23
  • It sounds like you're just after an adapter or adapters. You can have an adapter defined for "*" and then override it for more specific interfaces. – SteveM Feb 03 '15 at 16:31