How to register object factory adapters (schema.Object fields)

Hello everybody,

I developed a CT Person and a CT Student which has a field "person" of type schema.Object (schema=IPerson). I also registered a factory adapter (only for person) by using z3c.form.object.registerFactoryAdapter (see code below):

class IPerson(Interface):
"""Person Interface"""

form.mode(person_id='hidden')
person_id = schema.ASCIILine(
    title=_(u'label_person_id', default=u'Person ID'),
    max_length=45,
    required=False
)

last_name = schema.TextLine(
    title=_(u'label_last_name', default=u'Last Name'),
    required=True,
    max_length=45
)

first_name = schema.TextLine(
    title=_(u'label_first_name', default=u'First Name'),
    required=True,
    max_length=45
)

citizenship = schema.TextLine(
    title=_(u'label_citizenship', default=u'Citizenship'),
    required=False,
    max_length=45
)

street = schema.TextLine(
    title=_(u'label_street', default=u'Street'),
    required=False,
    max_length=45
)

house_number = schema.TextLine(
    title=_(u'label_house_number', default=u'House Number'),
    required=False,
    max_length=45
)

postal_code = schema.TextLine(
    title=_(u'label_postal_code', default=u'Postal Code'),
    required=False,
    max_length=45
)

location = schema.TextLine(
    title=_(u'label_location', default=u'Location'),
    required=False,
    max_length=45
)

email = schema.TextLine(
    title=_(u'label_email', default=u'E-Mail'),
    required=False,
    max_length=45
)

date_of_birth = schema.Date(
    title=_(u'label_date_of_birth', default=u'Date of Birth'),
    required=False
)

class Person(Item):
"""Class for Persons"""

implements(IPerson)

def __call__(self):
    if dbg_print:
        print('In allgmed.site.content.person.Person.__call__')
    db = getUtility(IDatabase, name="allgmed_db")

    session = db.session
    session.add(self)
    session.flush()
    session.expunge_all()

def getUID(self):
    uuid = IUUID(self, None)
    return uuid

registerFactoryAdapter(IPerson, Person)

class IStudent(Interface):
"""Interface for Students"""

matriculation_number = schema.TextLine(
    title=_(u'label_matriculation_number', default=u'Matriculation Number'),
    required=True,
    max_length=45
)

university = schema.TextLine(
    title=_(u'label_university', default=u'University'),
    required=True,
    max_length=45
)

person = schema.Object(
    title=_(u'label_person', default=u'Person'),
    schema=IPerson,
    required=True
)

class Student(Item):
"""Class for Students"""

implements(IStudent)

def __call__(self):
    if dbg_print:
        print('In allgmed.site.content.student.Student.__call__')
    db = getUtility(IDatabase, name="allgmed_db")
    session = db.session

    self.person.person_id = self.getUID()
    self.person()

    person_obj = self.person
    person_id = person_obj.person_id
    self.person = person_id

    session.add(self)
    session.flush()

    self.person = person_obj

    session.expunge_all()

def getUID(self):
    uuid = IUUID(self, None)
    return uuid

Everything is fine if I add a new student. When I try to edit the student by editing a person field the following error occurs:
ValueError: 'name is not a string or unicode'
There is no stacktrace, I got the error from the debugger.
It happens only if I edit a person field (e.g. last_name) within the student form and only for the first time I try to save the person. It works on the second save.

I found out that the error occurs when the IObjectModifiedEvent is triggered:

if names:
zope.event.notify(
zope.lifecycleevent.ObjectModifiedEvent(obj,
zope.lifecycleevent.Attributes(self.field.schema, *names)))

z3c.form.object (line 126)

I thought that maybe something is wrong with the object factory adapter. Can anybody help? Thanks in advance!

You have here a good example: https://jamaicandevelopers.com/Members/oshane--bailey/building-mosaic-tiles-subforms

1 Like

Hi Maik,

thanks for the tutorial. I think the adapter is ok now, but sometimes there are still problems with editing a student.

In case of an error, the data dictionary looks like this (person is mssing):
data => {'university': u'lmu', 'matriculation_number': u'23415'}
error => ValueError('name is not a string or unicode',)

Normally if no error occurs:
data => {'person': <Person at /allgmed/de/students/student/>, 'university': u'lmu', 'matriculation_number': u'23415'}

It only happens when I edit a person field (within the ObjectWidget) and not every time.