Search code examples
plonedexterity

Plone: Dexterity Model driven content type with collective.z3cform.datagridfield - nesting an xml model in an xml model


I'd like to create a content type that uses collective.z3cform.datagridfield. I have been trying to follow the example mentioned in this presentation: http://glicksoftware.com/presentations/dexterity-in-the-wild

The example demonstrates referencing a second schema from the main schema. This is my attempt.

<?xml version="1.0" ?>
<model xmlns="http://namespaces.plone.org/supermodel/schema"
       xmlns:form="http://namespaces.plone.org/supermodel/form">
<field name="telephone" type="zope.schema.List"
    form:widget="collective.z3cform.datagridfield.DataGridFieldFactory">

      <title>Telephone</title>
      <description>Enter telephone numbers here</description>
      <max_length>10</max_length>
      <min_length>2</min_length>
      <missing_value/>
      <readonly>False</readonly>
      <required>False</required>

      <value_type type="collective.z3cform.datagridfield.DictRow">
        <title>Number</title>
        <schema>mypackage.mytype.IPhoneSchema</schema>
      </value_type>
    </field>
</schema>
</model>

I define my second schema in mypackage/mytype.py to look like this:

from plone.supermodel import model

class IPhoneSchema(model.Schema):
    """Schema for dict rows used in DataGridFields
    name is the 'real'  name
    token is the token used in the vocabularies
    """
#
    model.load("models/phone.xml")

And then in models/phone.xml I have the following:

<?xml version="1.0" ?>
<model xmlns="http://namespaces.plone.org/supermodel/schema"
       xmlns:form="http://namespaces.plone.org/supermodel/form">

    <schema>
         <field name="number" type="zope.schema.TextLine"> 
            <description/> 
            <required>False</required> 
            <title>Number</title> 
        </field> 
    </schema>
</model>

When I start Plone I get the following error:

 SupermodelParseError: 'module' object has no attribute 'mytype'
      <schema>mypackage.mytype.IPhoneSchema</schema>

Solution

  • Effectively, both model xml files were defined in the mytype.py file. This caused an issue at runtime where mytype.py cannot call ... mytype.IPhoneSchema.

    The solution was to create a phoneschema.py file independent of mytype.py with the following content:

    from plone.supermodel import model

    class IPhoneSchema(model.Schema):

       """Schema for dict rows used in DataGridFields
       they are used for individual phone numbers
       """
    
       model.load("models/phone.xml")
    

    Instead of calling mytype.IPhoneSchema, we can now call phoneschema.IPhoneSchema. I can include the phone schema in models/mytype.xml (see the example below).

    ...

      <title>Telephone</title>
      <description>Enter telephone numbers here</description>
      <max_length>10</max_length>
      <min_length>2</min_length>
      <missing_value/>
      <readonly>False</readonly>
      <required>False</required>
    
      <value_type type="collective.z3cform.datagridfield.DictRow">
        <title>Number</title>
        <schema>mypackage.mytype.IPhoneSchema</schema>
      </value_type>
    </field> 
    

    ...

    Here's a diagram of the relationship, phoneschema.py 'loads' phone.xml:

    phoneschema.py loads phone.xml

    For the sake of reference, the file tree of my product now would look something like this (I've put a star next to the key file in this scenario phoneschema.py which references the second model file):

    ...
    ├── __init__.py
    ├── configure.zcml
    ├── mytype.py
    ├── mytype_templates
    │   └── view.pt
    ├── models
    │   ├── mytype.xml
    │   └── phone.xml
    ├── *phoneschema.py
    ...