collective.exportimport
collective.exportimport copied to clipboard
Exporting portlet with relation field fails
I have several relation fields in a portlet. Exporting portlets then fails because a RelationValue
is not json serialisable:
http://localhost:9152/plone/@@export_portlets
Traceback (innermost last):
Module ZPublisher.Publish, line 138, in publish
Module ZPublisher.mapply, line 77, in mapply
Module ZPublisher.Publish, line 48, in call_object
Module collective.exportimport.export_other, line 477, in __call__
Module json, line 251, in dumps
Module json.encoder, line 209, in encode
Module json.encoder, line 431, in _iterencode
Module json.encoder, line 332, in _iterencode_list
Module json.encoder, line 408, in _iterencode_dict
Module json.encoder, line 408, in _iterencode_dict
Module json.encoder, line 332, in _iterencode_list
Module json.encoder, line 408, in _iterencode_dict
Module json.encoder, line 408, in _iterencode_dict
Module json.encoder, line 442, in _iterencode
Module json.encoder, line 184, in default
TypeError: <z3c.relationfield.relation.RelationValue object at 0x114132050> is not JSON serializable
A bit related Is this comment from Philip where he removes some relations code, although I guess this was only active when exporting content, and not portlets.
The following diff in the portlet export code fixes it for me:
$ git diff
diff --git a/src/collective/exportimport/export_other.py b/src/collective/exportimport/export_other.py
index f358a1c..383635a 100644
--- a/src/collective/exportimport/export_other.py
+++ b/src/collective/exportimport/export_other.py
@@ -535,13 +535,18 @@ def export_local_portlets(obj):
settings = IPortletAssignmentSettings(assignment)
if manager_name not in items:
items[manager_name] = []
+ from z3c.relationfield.relation import RelationValue
+ assignment_data = {}
+ for name in schema.names():
+ value = getattr(assignment, name, None)
+ if value and isinstance(value, RelationValue):
+ value = value.to_object.UID()
+ assignment_data[name] = value
+
items[manager_name].append({
'type': portlet_type,
'visible': settings.get('visible', True),
- 'assignment': {
- name: getattr(assignment, name, None)
- for name in schema.names()
- },
+ 'assignment': assignment_data,
})
return items
The code needs to be more robust, but those are details. I am not sure if this is a reasonable place for this fix or if there is a more general place.
Ah, wait, using this works too:
json_compatible(getattr(assignment, name, None))
At least then you get an export without errors, although my earlier code that returns uuids could be preferable in some cases.
With the json_compatible
call, the export will have a field value in the assignment like this:
"linkitem1": {
"@type": "Document",
"review_state": "published",
"@id": "http://localhost:9152/plone/water",
"description": "",
"title": "Water"
}
The current portlet importer stores this dictionary directly in the field, instead of turning it into a RelationValue
. So the portlet view gives an error and the edit form does not show any current value.
Turning it into a UID during export, makes it easier on the import side. For best results, I do have to change my portlet template, and change my portlet field definition from RelationChoice
to schema.Choice
, and use the RelatedItemsFieldWidget
. But this particular portlet has 6 relation fields, linkitem1 though linkitem6, so I may want to refactor it to a single schema.List
anyway.
BTW, I am very happy that I can look at example.contenttype
for the various field/widget combinations.
So: shall I make a PR for the portlet export that changes relation values into uuids?
if value and isinstance(value, RelationValue):
value = value.to_object.UID()