Hi!
Context: add DGF (List with Multiwidget with DictRows from DGF)
if someone arrives here to try to get some info on DGF and widget settings, my 2 cents:
- there's some differences if you're working on xml model and a file system schema
If you're just using DGF row schema without a form wrapper, widget settings does not work.
With this plain schema for the DGF row:
from collective.z3cform.datagridfield.datagridfield import DataGridFieldWidgetFactory
class ITableRowSchema(model.Schema): # can work with interface.Interface?
one = schema.TextLine(title=u"One")
two = schema.TextLine(title=u"Two")
three = schema.TextLine(title=u"Three")
class IFormSchema(interface.Interface):
table = schema.List(
title=u"Table",
value_type=DictRow(
title=u"tablerow",
schema=ITableRowSchema,
),
)
widget('table', DataGridFieldFactory, allow_reorder=True)
For example, if you add this field xml in the model editor:
<field name="mygrid" type="zope.schema.List">
<description></description>
<required>False</required>
<title>My Grid</title>
<form:widget type="myproduct.interfaces.DataGridFieldFactory"/>
</field>
widget settings like allow_reorder=True are ignored. The works if you use your own DataGridFieldFactory:
<form:widget type="myproduct.interfaces.MyDataGridFieldFactory"/>
in the xml above, and you create your factory (see below). At this point, the above IFormSchema is useless because the xml has already all the info to manage the field and the widget.
- if you are using DGF and using the editor in the dexterity control panel to add fields using xml, in the example above you've also to add this:
@adapter(schema.interfaces.IField, form.interfaces)
@implementer(form.interfaces.IFieldWidget)
def MyDataGridFieldFactory(field, request):
"""
A special widget constructor setting up widget parameters for DGF.
"""
if field.value_type is None:
# render the widget in the model editor if the schema is missing
field.value_type=DictRow(schema=ITableRowSchema)
widget = DataGridFieldWidgetFactory(field, request)
widget.allow_reorder = True
widget.auto_append = False
return widget
the lines if field.value_type is None:
are meant to avoid an error when rendering the dexterity content type editor the next time you add another field TTW. The error happen only when you add a new field TTW because the field attribute value_type
is None. But you can change the xml and load it from filesystem using the profile. The problem here is that there's no schema for the field other than the one in the schema model editor. So, next time you will add a field, the DictRow serializer here:
https://github.com/collective/collective.z3cform.datagridfield/blob/b8f83531aad8f46678f1db715ded5d890ce68393/src/collective/z3cform/datagridfield/supermodel.py#L1-L5 cannot handle the value_type DictRow export (it is 2011 old code). It also misses the IFieldExportImportHandler interface to be picked up by the serializer, the call handler = queryUtility(IFieldExportImportHandler, name=value_fieldType)
return none instead of return the ObjectHandler
serializer.
With the code above, you can still define your content type TTW without using a file system schema and then add/remove/change fields in your content type, while using DGF.
- if you're working in a control panel configlet, you need something like this:
profiles/registry.xml:
<record name="my_grid_field" field="my_grid_field">
<field type="plone.registry.field.List">
<title></title>
<required>False</required>
</field>
<value_type type="collective.z3cform.datagridfield.registry.DictRow">
<required>False</required>
</value_type>
<value/>
</record>
and this in the configlet schema:
lista_servizi = schema.List(
title=u"",
value_type=DictRow(
title=u"tablerow",
schema=ITableRowMySchema,
),
)
directives.widget("my_grid_field", DataGridFieldFactory,
allow_reorder=True,
auto_append = False)
here the directives on widgets works because you've a fs schema that is loaded and then it is easier, z3c.form/plone.autoform and friends will do the boring and complex part.
Why this post? The point is that usually you've your schema in the product and if you add a field using the editor, Dexterity will merge your filesystem schema with the TTW schema and this should the way to go. The method I've shown above can be tricky BUT can be something that someone can/will fall into. So this post is to give you a direction on what is happening if errors like AttributeError: 'NoneType' object has no attribute 'schema'
or widget settings not working or zope.schema._bootstrapinterfaces.WrongType: ([<some dicts here>], <class 'str'>, 'value')
or TypeError: There is no persistent field equivalent for the field myfield of type 'List'.
Schema are powerful but can be really tricky. My suggestion is to follow the best practices and use plonecli to create boilerplates (interfaces, schema, etc) and search in the github collective for products using DataGridField.