mtrebron
(Norbert )
April 23, 2020, 6:02pm
1
Stupid question... What are the implications of:
registering a behavior without a factory, as documented here: https://docs.plone.org/external/plone.app.dexterity/docs/behaviors/schema-only-behaviors.html#storing-attributes - which causes the attribute to be stored directly on the object
vs
the structure provided by mr.bob seen here https://github.com/plone/bobtemplates.plone/blob/master/bobtemplates/plone/behavior.py and here https://github.com/plone/bobtemplates.plone/blob/master/bobtemplates/plone/behavior/behaviors/%2Bbehavior_name_normalized%2B.py.bob - which includes a marker interface and registration of the factory and marker, and therefore requires an explicit property getter and setter?
1 Like
zopyx
(Andreas Jung)
April 24, 2020, 4:58am
2
My approach is using this context_property()
factory in behaviors:
def context_property(name, default=None):
def getter(self, default=default):
return getattr(self.context, name, default)
def setter(self, value):
setattr(self.context, name, value)
def deleter(self):
delattr(self.context, name)
return property(getter, setter, deleter)
```
...
```
@implementer(IGuideline)
@adapter(IConnector)
class Guideline(object):
""" Adapter for Guideline """
def __init__(self, context):
self.context = context
notice = context_property("notice")
portlet_text = context_property("portlet_text", "")
portlet_title = context_property("portlet_title", "")
gl_language = context_property("gl_language")
gl_state = context_property("gl_state", "")
gl_date = context_property("gl_date", "")
```
3 Likes
zopyx
(Andreas Jung)
April 24, 2020, 7:58am
3
Disclaimer : the handling of default
value is possible wrong and not working in reality.
mtrebron
(Norbert )
April 25, 2020, 9:35am
4
Looks like others have wondered about this situation before:
opened 09:30AM - 25 Aug 17 UTC
closed 07:00AM - 13 Apr 23 UTC
01 type: bug
31 needs: help
05 type: question
14 prio: low
i had troubles with a behavior i wrote and stumbled a bit more than usual ;)
… behavior.py
```python
#schema
@provider(IFormFieldProvider)
class IMyBehavoir(model.Schema):
field1 = ...
field2 = ...
#factory
@implementer(IMyBehavior)
class MyBehavior(object):
def __init__(self, context):
self.context = context
@property
def attr1(self):
return 'test'
```
configure.zcml
```xml
<plone:behavior
name="my.behavior"
provides=".behavior.IMyBehavior"
factory=".behavior.MyBehavior"
marker=".behavior.IMyBehavior"
/>
```
on a view (self.context is a plone content object i applied the behavior to - which worked easily), when calling the attr1 i get an error:
```
>>> IMyBehavior(self.context).attr1
*** AttributeError: 'MyBehavior' object has no attribute 'attr1'
```
ok... not really a helpful traceback, but with a little hint from @agitator i changed the marker as it is not allowed to be the same interface as the schema interface:
behavior.py
```python
#marker
class IMyBehavoirMarker(Interface):
pass
#schema
@provider(IFormFieldProvider)
class IMyBehavoir(model.Schema):
field1 = ...
field2 = ...
#factory
@implementer(IMyBehavior)
class MyBehavior(object):
def __init__(self, context):
self.context = context
@property
def attr1(self):
return 'test'
```
configure.zcml
```xml
<plone:behavior
name="my.behavior"
provides=".behavior.IMyBehavior"
factory=".behavior.MyBehavior"
marker=".behavior.IMyBehaviorMarker"
/>
```
```
>>> IMyBehavior(self.context).attr1
test
```
it works! yeah!
but then i thought about, why to have a marker interface at all, as i never touch or use it.
so i removed it completely:
behavior.py
```python
#schema
@provider(IFormFieldProvider)
class IMyBehavoir(model.Schema):
field1 = ...
field2 = ...
#factory
@implementer(IMyBehavior)
class MyBehavior(object):
def __init__(self, context):
self.context = context
@property
def attr1(self):
return 'test'
```
configure.zcml
```xml
<plone:behavior
name="my.behavior"
provides=".behavior.IMyBehavior"
factory=".behavior.MyBehavior"
/>
```
```
>>> IMyBehavior(self.context).attr1
test
```
and it works **too**!
## so here are my confusion:
1.) why do i need a marker interface at all here?
2.) why does the traceback not tell me anything about it?
3.) why doesn't zope scream about it on start up?
4.) why is the documentation miss leading at that point?
to be more precise on 4.)
quoted from the docs:
> marker
> A marker interface to be applied by the behavior. If factory is not given, then this is optional and defaults to the value of provides. If factory is given **marker is required** and **should** be different from provides - **even if its not enforced**.
"marker is required" -> NO. it's not. my sample above shows that. no errors, no problems, functionality works fine.
"should" -> NO. it has to be (**if defined**). my sample shows that.
"even if its not enforced" -> NO. it **is** enforced.
and here comes my biggest confusion on this whole topic:
keeping in mind that the marker is optional and falls back to provides...
why does the code/registry work without a marker, but not with the same interface given???
isn't it basically the same "stunt"?
no marker value -> use provides value --> works
marker value same as provides value --> works NOT
... this gives me some headache and i hope, someone can help me to understand ;)
(i am not even sure if i found a bug or i am the bug - kafkaesque, isn't it ...)
answered by @jensens - Cheers!
At the end the simple rule:
If you use schema only, the context provides the schema interface (because it stores the values as attributes)
If you use behavior-adapters, always provide a marker. Its semantically correct. If you use the adapters interface as a marker it might work, but its semantically wrong.
and a rather good explanation exists in the docs...
1 Like