RelationList with SelectFieldWidget: ValueError: Circular reference detected

I created a content type with a RelationList field with a SelectFieldWidget. This content type is inside a Dexterity folder type (which I also created). The code of the content type for the RelationList field:


    speaker = RelationList(
        title=_(safe_unicode('Presenter')),
        default=[],
        value_type=RelationChoice(vocabulary='ConferenceSpeaker'),
        required=False,
        missing_value=[],
    )
    directives.widget(
        'speaker',
        SelectFieldWidget,
    )

I use a vocabulary for this field (named utility in configure.zcml):


    <utility
        name="ConferenceSpeaker"
        component=".vocabularies.ConferenceSpeakerVocabularyFactory" />

The code of the vocabulary:


@implementer(IVocabularyFactory)
class ConferenceSpeakerVocabulary(object):

    def __call__(self, context=None):
        terms = []
        for brain in api.content.find(portal_type='collective.conferences.conferencespeaker', sort_on='sortable_title'):
            terms.append(SimpleTerm(
                value=brain.getObject(),
                token=brain.UID,
                title=safe_unicode('{0} ({1})').format(brain.Title, brain.getPath()),
            ))
        return SimpleVocabulary(terms)


ConferenceSpeakerVocabularyFactory = ConferenceSpeakerVocabulary()

And the code of the conferencespeaker module:


# -*- coding: utf-8 -*-
from collective.conferences import _
from collective.conferences.common import allowedconferenceimageextensions
from collective.conferences.common import validateEmail
from collective.conferences.common import validateimagefileextension
from collective.conferences.common import validatePhoneNumber
from plone import api
from plone.app.textfield import RichText
from plone.autoform import directives
from plone.namedfile.field import NamedBlobImage
from plone.supermodel import model
from Products.CMFPlone.utils import safe_unicode
from Products.Five import BrowserView
from zope import schema


class IConferenceSpeaker(model.Schema):

    lastname = schema.TextLine(
        title=_(safe_unicode('Last name')),
        required=True,
    )

    firstname = schema.TextLine(
        title=_(safe_unicode('First name')),
        required=True,
    )

    street = schema.TextLine(
        title=_(safe_unicode('Street')),
        description=_(safe_unicode(
            'For those requiring visa, please add your full postal address details')),
        required=False,
    )

    city = schema.TextLine(
        title=_(safe_unicode('City')),
        description=_(safe_unicode(
            'For those requiring visa, please add your full postal address details')),
        required=False,
    )

    postalcode = schema.TextLine(
        title=_(safe_unicode('Postal Code')),
        description=_(safe_unicode(
            'For those requiring visa, please add your full postal address details')),
        required=False,
    )

    country = schema.TextLine(
        title=_(safe_unicode('Country')),
        description=_(safe_unicode(
            'For those requiring visa, please add your full postal address details')),
        required=False,
    )

    email = schema.ASCIILine(
        title=_(safe_unicode('Your email address')),
        constraint=validateEmail,
        required=True,
    )

    telephonenumber = schema.TextLine(
        title=_(safe_unicode('Telephone Number')),
        description=_(safe_unicode(
            'Please fill in your telephone number so that we could get in contact with you by phone if necessary.')),
        constraint=validatePhoneNumber,
        required=False,
    )
    mobiletelepone = schema.TextLine(
        title=_(safe_unicode('Mobile Telephone Number')),
        description=_(safe_unicode(
            'Please fill in your mobile telephone number so that we could get in contact with you '
            'during the conference.')),
        constraint=validatePhoneNumber,
        required=True,
    )

    organisation = schema.TextLine(
        title=_(safe_unicode('Organisation')),
        required=False,
    )

    description = schema.Text(
        title=_(safe_unicode('A short bio')),
        required=True,
    )

    bio = RichText(
        title=_(safe_unicode('Bio')),
        required=False,
    )

    directives.mode(speakerpicture='display')
    speakerpicture = schema.TextLine(
        title=_(safe_unicode(
            'The following file extensions are allowed for the picture '
            'files (upper case and lower case and mix of both):')),
        defaultFactory=allowedconferenceimageextensions,
    )

    picture = NamedBlobImage(
        title=_(safe_unicode('Picture')),
        description=_(safe_unicode('Please upload an image')),
        constraint=validateimagefileextension,
        required=False,
    )


def notifyUser(self, event):
    user = api.user.get_current()
    sender = api.portal.get_registry_record(
        'plone.email_from_address')
    email = user.getProperty('email')

    if not sender:
        return

    subject = 'Is this you?'
    message = 'A speaker / leader of a workshop called {0} was added here {1}. If ' \
              'this is you, everything is fine.'.format(self.title, self.absolute_url())

    api.portal.send_email(
        recipient='{0}'.format(email),
        sender='{0}'.format(sender),
        subject='{0}'.format(subject),
        body='{0}'.format(message))


class ConferenceSpeakerView(BrowserView):

    def talks_of_speaker(self):
        catalog = api.portal.get_tool(name='portal_catalog')

        # execute a search
        results = catalog(speakertalk='Talk 1')
        # examine the results
        for brain in results:
            url = brain.getURL()

        return url

    def talks_of_speaker2(self):
        catalog = api.portal.get_tool(name='portal_catalog')

        # execute a search
        results = catalog(portal_type='collective.conferences.talk',
                          review_state='published')
        # examine the results
        for brain in results:
            title = brain.Title

        return title

    def speakertalks(self):

        speakername = self.context.title
        results = api.content.find(portal_type='collective.conferences.talk',
                                   review_state='published',
                                   presenters=speakername)

        objects = []

        for brain in results:
            objects.append(brain)

        return objects

    def speakerworkshops(self):

        speakername = self.context.title
        results = api.content.find(portal_type='collective.conferences.workshop',
                                   review_state='published',
                                   workshopleader=speakername)

        objects = []
i
        for brain in results:
            objects.append(brain)

If I call the folder_contents view for the parent of my content objects, Plone is trying to find the contents of the folder but it never shows some results although it is showing that it is busy. I got the following traceback:

2020-08-05 10:39:42,463 ERROR   [Zope.SiteErrorLog:251][waitress] 1596616782.46239830.6521426565906934 http://localhost:8080/Plone/@@getVocabulary
Traceback (innermost last):
  Module ZPublisher.WSGIPublisher, line 156, in transaction_pubevents
  Module ZPublisher.WSGIPublisher, line 338, in publish_module
  Module ZPublisher.WSGIPublisher, line 256, in publish
  Module ZPublisher.mapply, line 85, in mapply
  Module ZPublisher.WSGIPublisher, line 62, in call_object
  Module plone.app.content.browser.vocabulary, line 266, in __call__
  Module plone.app.content.utils, line 24, in json_dumps
  Module simplejson, line 412, in dumps
  Module simplejson.encoder, line 296, in encode
  Module simplejson.encoder, line 378, in iterencode
ValueError: Circular reference detected
2020-08-05 10:39:42,566 ERROR   [Zope.SiteErrorLog:251][waitress] 1596616782.56519940.1180242857521221 http://localhost:8080/Plone/@@getVocabulary
Traceback (innermost last):
  Module ZPublisher.WSGIPublisher, line 156, in transaction_pubevents
  Module ZPublisher.WSGIPublisher, line 338, in publish_module
  Module ZPublisher.WSGIPublisher, line 256, in publish
  Module ZPublisher.mapply, line 85, in mapply
  Module ZPublisher.WSGIPublisher, line 62, in call_object
  Module plone.app.content.browser.vocabulary, line 266, in __call__
  Module plone.app.content.utils, line 24, in json_dumps
  Module simplejson, line 412, in dumps
  Module simplejson.encoder, line 296, in encode
  Module simplejson.encoder, line 378, in iterencode
ValueError: Circular reference detected

The issue seemed only appear with the RelationLIst - SelectFieldWidget combination. I got no such issue with the combination of a RelationList fields with a RadioFieldWidget. But in the case of my content type I need the option to select not only one relation.

Thanks for any hints for a solution.
Andreas

Can you provide the related JSON?

I have been using this monkey patch for a long time:

  <monkey:patch description="Fix circular reference when field is RelationValue"
        class="plone.app.content.utils"
        original="custom_json_handler"
        replacement=".monkey_patches.pac_utils_custom_json_handler"
        />
def pac_utils_custom_json_handler(obj):
    #log.info('%s custom_json_handler called' % get_linenumber())
    if obj == Missing.Value:
        return None
    if type(obj) in (datetime.datetime, datetime.date):
        return obj.isoformat()
    if type(obj) == DateTime:
        dt = DateTime(obj)
        return dt.ISO()
    if type(obj) == set:
        return list(obj)
    # 20170418 fixed Circular reference
    # return the id of the related object(s)
    if type(obj) == RelationValue:
        return obj.to_path.split('/')[-1]
    if type(obj) == RelationList:
        return list(item.to_path.split('/')[-1] for item in obj)
    return obj

1 Like

I don't use any special java script for the add-on. It runs on Plone
5.2.1 and Python 3.6.

How could I provide the related JSON? Could I get it via the inspector
from the webdeveloper tools of Firefox?

I have seen a report about this same problem quite recently in this forum. When I remember right, then the issue could be solved. Maybe, you search for the discussion.

I have experiment this error in someplaces when a brain contained an attribute non serializable.

My guess is the above line needs to be changed with the UID like you do with the token, later you can retrieve the object with the UID if needed