From f68fab7ea3209a096423d8567fa7f70a93786c7a Mon Sep 17 00:00:00 2001 From: Faakhir Zahid <110815427+Faakhir30@users.noreply.github.com> Date: Thu, 3 Oct 2024 02:00:40 +0500 Subject: [PATCH] Fix invalid values in RelationListFieldSerializer. (#1818) * Fix invalid values in serializer. * Add test to check deleted relations * use to_id attribute to check content's existance. --- news/1818.bugfix | 1 + src/plone/restapi/serializer/relationfield.py | 14 +++++++++- .../tests/test_dxcontent_serializer.py | 28 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 news/1818.bugfix diff --git a/news/1818.bugfix b/news/1818.bugfix new file mode 100644 index 0000000000..54260238ec --- /dev/null +++ b/news/1818.bugfix @@ -0,0 +1 @@ +Fix response of `RelationListFieldSerializer` by filtering out invalid items. @Faakhir30 diff --git a/src/plone/restapi/serializer/relationfield.py b/src/plone/restapi/serializer/relationfield.py index 28483ce424..d54cadb9d1 100644 --- a/src/plone/restapi/serializer/relationfield.py +++ b/src/plone/restapi/serializer/relationfield.py @@ -33,4 +33,16 @@ class RelationChoiceFieldSerializer(DefaultFieldSerializer): @adapter(IRelationList, IDexterityContent, Interface) @implementer(IFieldSerializer) class RelationListFieldSerializer(DefaultFieldSerializer): - pass + def get_value(self, default=[]): + """Return field value reduced to list of non-broken Relationvalues. + + Args: + default (list, optional): Default field value. Defaults to empty list. + + Returns: + list: List of RelationValues + """ + value = super().get_value() + if not value: + return [] + return [el for el in value if el.to_id] diff --git a/src/plone/restapi/tests/test_dxcontent_serializer.py b/src/plone/restapi/tests/test_dxcontent_serializer.py index 6fd514956f..567aa01e62 100644 --- a/src/plone/restapi/tests/test_dxcontent_serializer.py +++ b/src/plone/restapi/tests/test_dxcontent_serializer.py @@ -27,6 +27,10 @@ from zope.component import provideAdapter from zope.component import queryUtility from zope.interface import Interface +from z3c.relationfield import RelationValue +from z3c.relationfield.event import _setRelation +from zope.component import getUtility +from zope.intid.interfaces import IIntIds from zope.publisher.interfaces.browser import IBrowserRequest from importlib import import_module @@ -191,6 +195,30 @@ def test_serializer_includes_expansion(self): "foo", ) + def test_serializer_excludes_deleted_relations(self): + + intids = getUtility(IIntIds) + self.portal.invokeFactory( + "DXTestDocument", + id="doc2", + ) + rel1 = RelationValue(intids.getId(self.portal.doc1)) + rel2 = RelationValue(intids.getId(self.portal.doc2)) + self.portal.doc1.test_relationlist_field = [ + rel1, + rel2, + ] + _setRelation(self.portal.doc1, "test_relationlist_field", rel1) + _setRelation(self.portal.doc1, "test_relationlist_field", rel2) + # delete doc2 to make sure we have a None value in the relation list + self.portal.manage_delObjects(["doc2"]) + + obj = self.serialize() + self.assertEqual(1, len(obj["test_relationlist_field"])) + self.assertEqual( + "http://nohost/plone/doc1", obj["test_relationlist_field"][0]["@id"] + ) + def test_get_is_folderish(self): obj = self.serialize() self.assertIn("is_folderish", obj)