getMultiadapter error with custom EditForm in dexterity plone 4.2 - plone

I'm trying to open a custom editform which would only show one field from a form with 12 fields:
class EditForm(dexterity.EditForm):
grok.name('editCustom')
grok.context(IInfo)
def updateWidgets(self):
super(EditForm, self).updateWidgets()
self.widgets['alps'].mode = 'hidden'
self.widgets['operationStatus'].mode = 'hidden'
# etc.
things work fine until I get to a field which is a MultiField list-choice:
self.widgets['siteContact'].mode = 'hidden'
(this is the entry in the form.Schema):
siteContact = schema.List(
title=_(u"Site Contact"),
description =_(u"Select Site Contacts"),
value_type=schema.Choice(vocabulary=("test.siteContact")),
required=False,
)
but when I try to access the custom EditForm I get:
Module z3c.form.widget, line 140, in render
Module zope.component._api, line 109, in getMultiAdapter
ComponentLookupError: ((<Container at /test/first>, <HTTPRequest, URL=http://localhost:8080/test/first/##editCustom>, <Products.Five.metaclass.EditForm object at 0x08F9D9F0>, <zope.schema._field.List object at 0x084844B0>, <OrderedSelectWidget 'form.widgets.siteContact'>), <InterfaceClass zope.pagetemplate.interfaces.IPageTemplate>, 'hidden')

I ran into the exact same problem and don't know if there's a fix or work around by now but if ordering doesn't matter, you could try schema.Set (or schema.FrozenSet) instead of schema.List. These all let you select multiple options. I've tested the Set options and they both work with the mode as hidden.
(1) Set/FrozenSet
field = zope.schema.Set(
value_type=zope.schema.Choice(values=(1, 2, 3, 4)),
default=set([1, 3]) )
widget = setupWidget(field)
widget.update()
widget.__class__
<class 'z3c.form.browser.select.SelectWidget'>
select widget: allows you to select one or more values from a set of given options
(2) List
field = zope.schema.List(
value_type=zope.schema.Choice(values=(1, 2, 3, 4)),
default=[1, 3] )
widget = setupWidget(field)
widget.update()
widget.__class__
<class 'z3c.form.browser.orderedselect.OrderedSelectWidget'>
ordered-select: allows you to select one or more values from a set of given options and sort those options.

It looks like you might be specifying the vocabulary incorrectly. Please change
schema.Choice(vocabulary=("test.siteContact"))
to
schema.Choice(vocabulary="test.siteContact")

Related

Rename Dexterity object (id) after copy

It's simple to choose the object ID at creation time with INameChooser.
But we also want to be able to choose the object ID after a clone (and avoid copy_of in object ID).
We tried several different solutions :
subscribers on events :
OFS.interfaces.IObjectClonedEvent
zope.lifecycleevent.interfaces.IObjectAddedEvent
...
manage_afterClone method on content class
Every time, we get a traceback because we changed the ID "too soon". For example when using Plone API :
File "/Users/laurent/.buildout/eggs/plone.api-2.0.0a1-py3.7.egg/plone/api/content.py", line 256, in copy
return target[new_id]
File "/Users/laurent/.buildout/eggs/plone.folder-3.0.3-py3.7.egg/plone/folder/ordered.py", line 241, in __getitem__
raise KeyError(key)
KeyError: 'copy_of_87c7f9b7e7924d039b832d3796e7b5a3'
Or, with a Copy / Paste in the Plone instance :
Module plone.app.content.browser.contents.paste, line 42, in __call__
Module OFS.CopySupport, line 317, in manage_pasteObjects
Module OFS.CopySupport, line 229, in _pasteObjects
Module plone.folder.ordered, line 73, in _getOb
AttributeError: 'copy_of_a7ed3d678f2643bc990309cde61a6bc5'
It's logical, because the ID is stored before events notification / manage_afterClone call for later use.
Even defining a _get_id on containers cannot work to define the ID, because we don't have the object to get attributes from (and generate the ID).
But then, how could we achieve this in a clean way ?
Please tell me there is a better solution than redefining _pasteObjects (OFS.CopySupport) !
Thank you for your inputs !
So unfortunately not...
But you can access the original object from within _get_id.
For example:
from OFS.CopySupport import _cb_decode
from plone import api
...
def _get_id(self, id_):
# copy_or_move => 0 means copy, 1 means move
copy_or_move, path_segments = _cb_decode(self.REQUEST['__cp']
source_path = '/'.join(path_segments[0]) # Imlement for loop for more than one copied obj.
app = self.getPhysicalRoot()
# access to source obj
source_obj = app.restrictedTraverse(source_path)
# Do whatever you need - probably using INameChooser
....
To have a canonical way of patching this I use collective.monkeypatcher
Once installed add the following in ZCML:
<include package="collective.monkeypatcher" />
<monkey:patch
class="OFS.CopySupport.CopyContainer"
original="_get_id"
replacement="patches.patched_get_id"
/>
Where patches.py is your module containing the new method patched_get_id, which replaces _get_id.
I'm sorry I don't have better news for you, but this is how I solved a similar requirement.
This code (patched _get_id) adds a counter at the end of a id if already there.
def patched_get_id(self, id_)
match = re.match('^(.*)-([\d]+)$', id_)
if match:
id_ = match.group(1)
number = int(match.group(2))
else:
number = 1
new_id = id_
while new_id in self.objectIds():
new_id = '{}-{}'.format(id_, number)
number += 1
return new_id

How to access event data?

Click handlers in bokeh 1.0.3 used to have the signature attr, old, new. Now the are passed a single event object. How can I access its values?
menu = [('a', 'a'), ('b', 'b')]
dropdown = Dropdown(label='clickme', menu=menu)
def click_handler(event):
print(event)
returns
bokeh.events.MenuItemClick object at 0x7ff7de1cc208
EDIT: Where in the documentation is access to values of events described? I could not find anything on https://docs.bokeh.org/en/latest/docs/reference/events.html
I'm not sure where you got this information, but it's not quite correct. Dropdown was refined as a type of Button recently, so the ability to respond to the same kind of click events that other buttons do was added. But nothing was replaced. Callbacks for property changes work for any Bokeh object property, including Dropdown.value, and this has not changed:
from bokeh.io import curdoc
from bokeh.models import Dropdown
menu = [('a', 'a'), ('b', 'b')]
dropdown = Dropdown(label='clickme', menu=menu)
def cb(attr, old, new):
print(attr, old, new)
dropdown.on_change('value', cb)
curdoc().add_root(dropdown)
I'm coming in late, but I had the same issue as Leevi, and bigreddot's answer did not work for me (Bokeh 2.4.2, Python 3.10.2). Short answer is that a Bokeh Dropdown does not have a value attribute, so on_change('value, handler) will not work. The MenuItemClick object, has an item attribute, which is what you want.
menu = [('a', 'a'), ('b', 'b')]
dropdown = Dropdown(label='clickme', menu=menu)
def click_handler(event):
print(event.item)
dropdown.on_click(click_handler)
Long answer: I had the following code:
def choose_pipeline(event):
"""Change to the chosen pipeline"""
print(event)
pipeline_dropdown = Dropdown(label='Available Pipelines',
menu=[("Pipeline1", "pipeline1_value"),
("Pipeline2", "pipeline2_value")])
pipeline_dropdown.on_change('value', choose_pipeline)
and got this error:
File "C:\Users\me\PycharmProjects\DHLLDV\Scripts\SystemTab.py", line 69, in system_panel
pipeline_dropdown.on_change('value', choose_pipeline)
File "C:\Users\me\PycharmProjects\DHLLDV\venv\lib\site-packages\bokeh\model\model.py", line 434, in on_change
descriptor = self.lookup(attr)
File "C:\Users\me\PycharmProjects\DHLLDV\venv\lib\site-packages\bokeh\core\has_props.py", line 469, in lookup
raise AttributeError(f"{cls.__name__}.{name} property descriptor does not exist")
AttributeError: Dropdown.value property descriptor does not exist
I wrote this:
def choose_pipeline(event):
"""Change to the chosen pipeline"""
print(event.__dict__)
pipeline_dropdown = Dropdown(label='Available Pipelines', menu=[("Pipeline1", "pipeline1_value"),
("Pipeline2", "pipeline2_value")])
pipeline_dropdown.on_click(choose_pipeline)
And saw the result when I picked Pipeline2:
{'item': 'pipeline2_value', '_model_id': '1365'}

Plone Dexterity forms- Is it possible to set the default of a field equal to a value returned from a function in Add form?

I have a dexterity content type and I have been following along with the schema-driven types tutorial from the Dexterity Manual (http://docs.plone.org/external/plone.app.dexterity/docs/schema-driven-types.html#the-schema) and I am trying to change the default value of a field.
The value being returned is based on a portal catalog search. The field is an int type in which I want it equal to the highest number + 1 of the field in question. Because this is a field I want to hide, I figured maybe it would be possible to set the default value through returning a value from a function
value into the default parameter of a field.
Here is my Interface:
class ISupplier(model.Schema):
"""
Supplier of an asset or services
"""
supplier_id = schema.Int(
title=_(u"Supplier ID"),
description=_(u"ID that links to the database"),
required=True,
default=newID
)
...
Here is my function that I am trying to use to return a value. It is outside of classes.
#grok.provider(IContextSourceBinder)
def newID(context):
limit = 1
catalog = getToolByName(context, 'portal_catalog')
result = catalog.searchResults(portal_type='gpcl.supplier.supplier',
sort_on='supplier_id',
sort_order='descending',
sort_limit=limit
)[:1]
return result.supplier_id + 1
The reason I thought it'd be possible to do something like this is because in a choice type field I set the source equal to a value returned from a function:
form.widget(supplierType=CheckBoxFieldWidget)
supplierType = schema.List(title=u'Types',
value_type=schema.Choice(source=supplierTypes),
required=True
supplierTypes, the function, starts off like this:
#grok.provider(IContextSourceBinder)
def supplierTypes(context):
"""
"""
...
I did try using default_value:
#form.default_value(field=ISupplier['supplier_id'])
def supplierIDDefaultValue(data):
defaultID = newID
return defaultID
This did not work unfortunately. I believe I actually may be having a misunderstanding altogether. If I could have any help, I would greatly appreciate it.

SimpleTerm title not being set

I have a form with a SelectFieldWidget, that is currently using a static vocabularly, which is basically this:
from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
primary_contacts = SimpleVocabulary([
SimpleTerm( unicode(token), title=unicode(token.upper()), token=token ) for token in [
'one','two','three','four','five','six','seven','eight','nine','ten',
]
])
The vocabulary is assigned to the field in the form schema:
form.widget( primary_contact_person=SelectFieldWidget )
primary_contact_person = schema.List(
title = u'Nominate Primary Contact',
required = False,
value_type = schema.Choice(
vocabulary=primary_contacts,
)
)
The schema is then serialized using plone.supermodel & then deserialized when needed by the form (this is for another requirement).
The form is using a custom, handwritten template, and I'm in the process of adding the tal statements to generate the select field options. I had thought I could do this through referencing the widgets on the form, but when I do that I hit a problem:
(Pdb) self # break point in form
<Products.Five.metaclass.edit_metadata object at 0xc1ce450>
(Pdb) select = self.widgets['primary_contact_person']
(Pdb) first = [t for t in select.terms][0]
(Pdb) first.token
'one'
(Pdb) first.value
u'one'
(Pdb) first.title
(Pdb)
The title is None on the term when it's accessed through the widget. I've tried looking it up through the vocabulary:
(Pdb) select.terms.getTermByToken('one').title
(Pdb)
But again, it's None. However, it is there for terms in the original vocabulary object:
(Pdb) from my.package import primary_contacts
(Pdb) [t for t in primary_contacts][0].title
u'ONE'
So while I could use the source vocab object directly to provide the values the template needs, the plan is for this vocabulary to eventually be dynamic, at which point I would expect I'd need to interrogate the widget itself.
What am I doing wrong here, why is title not being defined?
The problem was with plone.supermodel. I should have mentioned more clearly that I'm using the serialized schema to produce the form, and I apologise for this.
Basically, plone.supermodel provides an export/import process, which can only deal with simple lists of values.
# line 263 in plone.supermodel.exportimport
term = SimpleTerm(token = encoded, value = value, title = value)
The solution was to use named vocabularies, which serializes the reference to the vocabulary rather than the vocabulary itself.
Sorry again for the lack of information that made this harder to debug.

Fulltext Search for Custom Type's TextField/RichWidget

Typing keyword in search box, say 日光 (Chinese words), I can see the live search result hinting the target items. It works well for types of Products.ATContentTypes (NewsItem, Page), but fails to find the same keyword for items of my custom type. Here is the partial code for the type:
atapi.TextField(
'history',
storage=atapi.AnnotationStorage(),
default_output_type='text/x-html-safe',
widget=atapi.RichWidget(
label=_(u"Establishment History"),
description=_(u"Enter Establishment History."),
rows = 20,
),
),
I do try add searchable=1, to the field, and re-catalog, but still no working. What am I missing?
Add the keyword searchable=True to your definition.
atapi.TextField(
'history',
searchable=True,
storage=atapi.AnnotationStorage(),
default_output_type='text/x-html-safe',
widget=atapi.RichWidget(
label=_(u"Establishment History"),
description=_(u"Enter Establishment History."),
rows = 20,
),
),

Resources