Using dexterity behaviors to provide a method - plone

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.

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.

Related

Python unittest Mock an object to not have an attribute

I have a function that runs some code if the object has a certain attribute, and in rare cases if if the object does not have the attribute, it runs different code. It is hard for me to create the object without the attribute for testing. I tried del instance.attribute but got an error. The attribute is actually a #property under the hood.
I have an object instance that has foo attribute. How does one mock it so that when one tries to access instance.foo it raises an AttributeError as usual if there is no attribute?
I tried mock.Mock(side_effect=AttributeError('Boom!')) but it only works with methods.
You could try to use a PropertyMock for the property, and generally you shall be able to set the respective side effect. Here is a simple working example:
from unittest import mock
import pytest
class Foo:
#property
def bar(self):
return "bar"
def test_no_foo():
bar_mock = mock.PropertyMock()
with mock.patch(f"{__name__}.Foo.bar", bar_mock):
bar_mock.side_effect = AttributeError('Boom!')
foo = Foo()
with pytest.raises(AttributeError):
foo.bar
As you patch the property in the class, not in the object, you can can also do this using patch.object if you have access to the object by accessing the class of the object:
def test_no_foo():
bar_mock = mock.PropertyMock()
foo = Foo()
with mock.patch.object(foo.__class__, "bar", bar_mock):
bar_mock.side_effect = AttributeError('Boom!')
with pytest.raises(AttributeError):
foo.bar

Plone- In a dexterity.EditForm why is attempting to disable a widget causing a ConstraintNotSatisfied error?

I'm trying to disable a widget in a dexterity.EditForm, but I'm getting an error .
Here is a part of my interface class with the particular widget I want to disable
class IRestaurant(IPlace):
restaurant_code = schema.TextLine(title=_(u""),
required=False,
)
IPlace is a form.Schema that IRestaurant inherits from. (from plone.directives)
Here is the code I for the dexterity.EditForm class:
class Edit(dexterity.EditForm):
grok.context(IRestaurant)
def updateWidgets(self):
super(Edit, self).updateWidgets()
self.widgets['restaurant_code'].disabled = True
When I go to the edit form, I get an error:
ConstraintNotSatisfied: True
Why is this error occurring and how can I fix this?
Also, the version of Plone I am using is Plone 4.3.5.
Edit: When I tried printing the type of object that self.widgets['restaurant_code'].disabled was, it said it was a NoneType object.
You might have better luck using the mode property.
Try something like this:
from z3c.form.interfaces import HIDDEN_MODE
def updateWidgets(self):
super(Edit, self).updateWidgets()
self.widgets['restaurant_code'].mode = HIDDEN_MODE

z3c.forms dynamic sources provider returns empty dictionary as a context object

I'm using Plone 4.1.4 and I'm trying to get dynamic sources for a schema.Choice to work, I need to populate country list which in turn depends on the context object.
I'm using this example:
http://plone.org/products/dexterity/documentation/manual/developer-manual/advanced/vocabularies
For IContextSourceBinder, example, an empty dictionary instead of actual context object is returned:
from zope import interface
from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
from zope.schema.interfaces import IContextSourceBinder
import zope.schema
from z3c.form import form
class CountryGenerator(object):
interface.implements(IContextSourceBinder)
def __call__(self, context):
#context is == {}
import pdb; pdb.set_trace()
return SimpleVocabulary([
SimpleTerm(value="not_selected", title=_("Country Not Selected"))
])
class IStep(interface.Interface):
region = schema.Choice(title=_("Select your country"),
required=True,
source=CountryGenerator,
default="not_selected")
class Step(form.Form):
fields = field.Fields(IStep)
label = _("Step")
description = _("Select your country")
When debugging point is hit inside CountryGenerator.__call__() method and I examine context object, the latter turn out to be just an empty dictionary.
When I try to use named utility example in the article mentioned above, and the similar thing happens, context there is also {}.
Could anyone point me to what I might be doing wrong?
UPDATE
ZCML for the form wrapper that calls the form is
<browser:page
name="view"
for="Products.oldproduct.MyFolderishClass"
class=".file.RegionClass"
permission="zope2.View"
/>
Where RegionClass inherits from Form wrapper, could it be permission or traversal issue?
Because your source is a class, you need to instantiate it:
class IStep(interface.Interface):
region = schema.Choice(title=_("Select your country"),
required=True,
source=CountryGenerator(),
default="not_selected")
In certain circumstances, such as with using sub forms or complex form widgets (widget within a widget for list selections, etc), you need to follow the __parent__ pointers to a proper outer context for getting back to the Plone context.

Is there a way to extend Plone Dexterity's INameFromTitle behavior?

The project I am working on uses Plone's awesome Dexterity plugin. A couple of my custom content types have very specific names that must be computed. The way I had originally accomplished this before was by adding plone.app.content.interfaces.INameFromTitle as a behavior in the object's generic setup entry, per the manual's directions:
<?xml version="1.0"?>
<object name="avrc.aeh.cycle" meta_type="Dexterity FTI">
...
<property name="schema">myproject.mytype.IMyType</property>
<property name="klass">plone.dexterity.content.Item</property>
...
<property name="behaviors">
<element value="plone.app.content.interfaces.INameFromTitle" />
</property>
...
</object>
Then I created an adapter that would provide INameFromTitle:
from five import grok
from zope.interface import Interface
import zope.schema
from plone.app.content.interfaces import INameFromTitle
class IMyType(Interface):
foo = zope.schema.TextLine(
title=u'Foo'
)
class NameForMyType(grok.Adapter):
grok.context(IMyType)
grok.provides(INameFromTitle)
#property
def title(self):
return u'Custom Title %s' % self.context.foo
This method is very similar to that suggested in this blog post:
http://davidjb.com/blog/2010/04/plone-and-dexterity-working-with-computed-fields
Unfortunately, this method stopped working after plone.app.dexterity beta and now my content items don't have their names assigned properly.
Would anyone happen to know how to extend Dexterity's INameFromTitle behavior for very specific naming use-cases?
Your help is much appreciated, thanks!
You could try the following.
in interfaces.py
from plone.app.content.interfaces import INameFromTitle
class INameForMyType(INameFromTitle):
def title():
"""Return a custom title"""
in behaviors.py
from myproject.mytype.interfaces import INameForMyType
class NameForMyType(object):
implements(INameForMyType)
def __init__(self, context):
self.context = context
#property
def title(self):
return u"Custom Title %s" % self.context.foo
I generally prefer defining my adapters using ZCML; in configure.zcml
<adapter for="myproject.mytype.IMyType"
factory=".behaviors.NameForMyType"
provides=".behaviors.INameForMyType"
/>
but you could probably also use a grok.global_adapter.
I did it with a behavior, by adapting to INameFromTitle
in behaviors.py
class INameFromBrandAndModel(Interface):
""" Interface to adapt to INameFromTitle """
class NameFromBrandAndModel(object):
""" Adapter to INameFromTitle """
implements(INameFromTitle)
adapts(INameFromBrandAndModel)
def __init__(self, context):
pass
def __new__(cls, context):
brand = context.brand
model = context.modeltype
title = u'%s %s' % (brand,model)
inst = super(NameFromBrandAndModel, cls).__new__(cls)
inst.title = title
context.setTitle(title)
return inst
in behaviors.zcml or configure.zcml
<plone:behavior
title="Name from brand and model"
description="generates a name from brand and model attributes"
for="plone.dexterity.interfaces.IDexterityContent"
provides=".behavios.INameFromBrandAndModel"
/>
<adapter factory=".behaviors.NameFromBrandAndModel" />
Then disable INameFromTitle behavior in profiles/types/your.contenttype.xml.
Voila. This integrates very well and shows a proper title in the default view and navigation. Removing context.setTitle(title) from the adapter would just leave us with a proper id, but no title set.
This does not change the title autocratically after editing. I had, so far, no success with overriding the klass property of my content types, as often suggested.
If you define the title attribute in your schema, like:
class IBike(form.Schema):
"""A bike
"""
title = schema.TextLine(
title = _(u'Title'),
required = False,
)
you can easily change the title later. Hiding the title field in the addForm should be done, to avoid misunderstandings.

Pylons, SQlite and autoincrementing fields

Hey!
Just started working with Pylons in conjunction with SQLAlchemy, and my model looks something like this:
from sqlalchemy import Column
from sqlalchemy.types import Integer, String
from helloworld.model.meta import Base
class Person(Base):
__tablename__ = "person"
id = Column(Integer, primary_key=True)
name = Column(String(100))
email = Column(String(100))
def __init__(self, name='', email=''):
self.name = name
self.email = email
def __repr__(self):
return "<Person('%s')" % self.name
To avoid sqlite reusing id's that might have been deleted, I want to add AUTOINCREMENT to the column "id". I've looked through the documentation for sqlalchemy and saw that the sqlite_autoincrement can be issued.
An example where this attribute is given can be found here.
sqlite_autoincrement seems though to be issued when creating the table itself, and I just wondered how it can be supplied when using a declarative style of the model such as mine.
Try including a __table_args__ attribute with the arguments you would pass to Table constructors in the traditional (non-declarative) data definition style, e.g.:
class Person(Base):
__tablename__ = "person"
__table_args__ = {'sqlite_autoincrement': True}
If you have to include several arguments, use this form instead (dict has to be last):
__table_args__ = (
Unique('foo'),
# ...
{'sqlite_autoincrement': True}
)
From the Table configuration section of the Declarative SQLAlchemy documentation:
Table arguments other than the name, metadata, and mapped Column arguments are specified using the __table_args__ class attribute. This attribute accommodates both positional as well as keyword arguments that are normally sent to the Table constructor.

Resources