Wow, I can use DictRow again! I had ended up with schema.Object after all else failed.
Your insights so far have helped me a lot, thank you!
I have a minimal representation of my issue, a mvp content type that gets its data via SQLAlchemy.
When using schema.Object, I have make my content subscriptable like this:
def __getitem__(self, name):
""" We shall be subscriptable
"""
if safe_hasattr(self, name):
value = getattr(self, name)
return value
else:
raise AttributeError(name)
But using DictRow, this fails with TypeError: getattr(): attribute name must be string
That was an easy fix:
def __getitem__(self, name):
""" We shall be subscriptable
"""
field_name, field = schema.getFieldsInOrder(IMinimalRelated)[name]
if safe_hasattr(self, field_name):
value = getattr(self, field_name)
return value
else:
raise AttributeError(field_name)
And now the content is displayed as expected
The Date field is still displayed left-aligned, identical as when using schema.Object - so something still seems wrong in my approach. (image at bottom)
Here are my content object and schema:
from pnz.erpediem.core import Base
from sqlalchemy import Column
from sqlalchemy import ForeignKey
from sqlalchemy import types
from sqlalchemy import null
from sqlalchemy.orm import backref
from sqlalchemy.orm import relationship
from zope.component import adapter
from zope.interface import implementer
from zope.interface import provider
from zope.interface import alsoProvides
from ..interfaces.mvp import IMinimal
from ..interfaces.mvp import IMinimalRelated
from plone.base.utils import safe_hasattr
import logging
log = logging.getLogger(__name__)
@implementer(IMinimalRelated)
class MinimalRelated(Base):
""" Related data to test the SQLA Minimal Object
"""
__tablename__ = 'mvp_testdata'
id = Column(
types.Integer(),
default=999999,
nullable=False,
primary_key=True,
unique=True,
)
ace_code = Column(
types.Unicode(3),
ForeignKey('ftx_ace_codes.ace_code'),
default=u'',
nullable=False,
)
test_date = Column(
types.Date(),
default=u'',
nullable=False,
)
vocab_term = Column(
types.Unicode(45),
default=u'',
nullable=False,
)
def __init__(self, context):
self.context = context
def __getitem__(self, name):
""" We shall be subscriptable
"""
field_name, field = schema.getFieldsInOrder(IMinimalRelated)[name]
if safe_hasattr(self, field_name):
value = getattr(self, field_name)
return value
else:
raise AttributeError(field_name)
def __repr__(self):
return "<%s %s - %s>" % (self.__class__.__name__, self.id, self.ace_code,)
@implementer(IMinimal)
class Minimal(Base):
""" SQLA Minimal Object
"""
__tablename__ = 'ftx_ace_codes'
ace_code = Column(
types.Unicode(3),
default=u'',
nullable=False,
primary_key=True,
unique=True,
)
title = Column(
types.Unicode(255),
default=u'',
nullable=False,
index=True,
)
description = Column(
types.Unicode(1024),
default=null(),
)
related_test = relationship(
'MinimalRelated',
order_by=MinimalRelated.id,
foreign_keys=[MinimalRelated.ace_code,],
)
def __init__(self, context):
self.context = context
def __getitem__(self, name):
""" We shall be subscriptable
"""
field_name, field = schema.getFieldsInOrder(IMinimalRelated)[name]
if safe_hasattr(self, field_name):
value = getattr(self, field_name)
return value
else:
raise AttributeError(field_name)
def __repr__(self):
return "<%s %s - %s>" % (self.__class__.__name__, self.ace_code, self.title)
""" Minimal Viable Product (mvp) Interfaces
"""
from pnz.erpediem.core import _
from zope.interface import Interface
from zope import schema
from plone.supermodel import model
from plone.autoform import directives
from plone.autoform.interfaces import IFormFieldProvider
from zope.component import adapter
from zope.interface import implementer
from zope.interface import provider
from plone.app.z3cform.widget import DateFieldWidget
from collective.z3cform.datagridfield.datagridfield import DataGridFieldWidgetFactory
from collective.z3cform.datagridfield.interfaces import IDataGridFieldWidget
from collective.z3cform.datagridfield.row import DictRow
from datetime import date
class IMinimalContainer(Interface):
""" """
@provider(IFormFieldProvider)
class IMinimalRelated(model.Schema):
"""Form schema to test MinimalRelated objects
"""
id = schema.TextLine(
title = _(u'ID'),
required=True,
)
directives.widget('id', klass='float-end')
ace_code = schema.TextLine(
title = _(u'ACE Code'),
required=True,
)
directives.widget('ace_code', klass='float-end')
test_date = schema.Date(
title = _(u'Date'),
required=False,
default=date.today(),
)
# neither works
#directives.widget('test_date', DateFieldWidget, klass='text-end')
directives.widget('test_date', klass='text-end')
vocab_term = schema.Choice(
title = _(u'Vocabulary Choice'),
vocabulary="pnz.erpediem.core.MinimalTestVocabulary",
required=False,
)
@provider(IFormFieldProvider)
class IMinimal(model.Schema):
""" Form schema for Minimal objects
"""
ace_code = schema.TextLine(
title = _(u'ACE Code'),
required=True,
)
title = schema.TextLine(
title = _(u'Title'),
required=False,
)
description = schema.TextLine(
title = _(u'Description'),
required=False,
)
related_test = schema.List(
title=_(u'Related Items'),
value_type=DictRow(
title=u'Table',
schema=IMinimalRelated,
),
default=[],
required=False,
)
directives.widget('related_test', DataGridFieldWidgetFactory)
"""
model.fieldset(
'related_items',
label=_(u'Related'),
fields=[
'related_test',
]
)
"""