Repository: plone/plone.restapi (language: python)

Issue:
relatedItems field returns None in the serializer if the related item is deleted
## Steps to reproduce

1. Create a content 1
2. Create a content 2
3. Edit content 1 and fill in the `relatedItems` field with content 2
4. Delete content 2
5. See content 1 serialization
6. `relatedItems` will be: `[ None ]`

This doesn't make sense to me. In the example, I think it's better to return an empty list. However, to do this, we would have to test the type of each element in a list [here](https://github.com/plone/plone.restapi/blob/f90329dfe49aa483517861a610b5175d68e0b6be/src/plone/restapi/serializer/converters.py#L100), to know if the value is a `RelationValue`. Or create a new adapter for the `IRelationList` field

Or should you handle this in the frontend?




Discussion / hints:
Good catch! 
The serializers of a RelationList field and RelationChoice field should return only non broken RelationValues.
I suggest the following:

```
@adapter(IRelationList, IDexterityContent, Interface)
@implementer(IFieldSerializer)
class RelationListFieldSerializer(DefaultFieldSerializer):
    def get_value(self, default=None):
        """Return field value reduced to list of non-broken Relationvalues.

        Args:
            default (list, optional): Default field value. Defaults to None.

        Returns:
            list: List of RelationValues
        """
        value = getattr(
            self.field.interface(self.context), self.field.__name__, default
        )
        return [el for el in value if el.to_object]
```

And accordingly for `RelationChoiceFieldSerializer`.

Fix patch:
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,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]


Test patch:
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)