[Zope3-Users] Re: How do I get the schema/field list from an instance?

Vinny vinny-mail-01+zope3users at palaceofretention.ca
Thu Feb 15 01:37:29 EST 2007


On Wed, 14 Feb 2007 19:22:53 +0100
Philipp von Weitershausen wrote:

> Vinny wrote:
> > In a ZPT I would do something like:
> > 
> > for field in fieldsInObject(row) # which has (ordered?) fields
> >   th cell: field.name
> > for row in rowView
> >   for field in fieldsInObject(row) # which has (ordered?) fields
> >     td cell: field.value
> > 
> > I know the above is broken in several ways.  I've been
> > trying various methods all evening.  The closest I get
> > is the __dict__ usage.  Ideally, I would get the 
> > interface provided by the object and feed that to 
> > form.Fields() but I can't determine how to get an
> > interface value that can be fed to form.Fields().
> > 
> > iface = zope.interface.providedBy(obj)
> > 
> > doesn't seem to work.
> 
> By itself it won't work. You'll have to do a bit more. First of all,
> an object can provide 0, 1 or more interfaces. Second, schemas are
> just interfaces whose specifications are fields (a schema can also
> have method specifications, there's no restriction to "mixing" a
> traditional interface with a schema). Therefore:
> 
>    for iface in zope.interface.providedBy(obj):
>        for name in iface:
>            field = iface[name]
>            if zope.schema.interfaces.IField.providedBy(field):
>                # you've got a schema field, now you can work with
>                # field.title, field.description, etc. (see IField)
> 


Thank you Philipp,

I have achieved my goal of a generic page template and
sql container view.  There are a few quick questions in the
example code below.  Mainly on being more efficient.  Here
are the results for everyone to use/comment on.  Note that
if anyone wants the rest of the files for this app., just
let me know and I'll see what I can arrange.  I still have
to test it with more tables.

========== iso639.py snippet =========
from zope.publisher.browser import BrowserView
from zope.security.proxy import removeSecurityProxy
from zope.app import zapi
import zope.interface
from zope.sequencesort import sort
from zope.app.preference import UserPreferences

class SQLAlchemyContainerView(BrowserView):

    def __init__(self, context, request):
        self.context = context
        self.request = request

    def rowView(self):
        """
        Presumably I have an instance of SQLAlchemyContainer
        We will be yielding a list of dictionaries with name and value keys.

        Philipp von Weitershausen provided some help on how to get to the
        schema/IField interface.

        """

        sqlRecordObjects = [sqlRecordObject for sqlRecordObject in self.context.values()]

        sort_on = (('lookup', 'nocase', 'asc'),)
        sorted_rows = sort(sqlRecordObjects, sort=sort_on)

        #userPreferences = UserPreferences()
#        print userPreferences.hello.sorting.sortGroups
        #sorted_rows = sort(sqlRecordObjects, sort=userPreferences.hello.sorting.sortGroups)

        for sqlRecordObject in sorted_rows:
            sqlRecordObject = removeSecurityProxy(sqlRecordObject)

            nameValues = []   # A list of two-term dictionaries
            for iface in zope.interface.providedBy(sqlRecordObject):
                for name in iface:
                    field = iface[name]
                    if zope.schema.interfaces.IField.providedBy(field):
                        # We've got a schema field, now we can work with
                        # field.title, field.description, etc. (see IField)

                        # Since we get a 'bonus' item in our schema called __name__
                        # we must ignore it.  We use a list containment condition in
                        # case we want to expand the list of ignored
                        if field.__name__ in ['__name__']:
                            pass
                        else:
                            #print (field.__name__, sqlRecordObject.__dict__[field.__name__], )
                            nameValues.append({'name': field.title, 'value': sqlRecordObject.__dict__[field.__name__]})
                            #print nameValues[key]

            # At this point we yield a list of dictionaries.
            # The dictionary has keys 'name' and 'value' and their values are,
            # respectively, the schema field title and
            # the value of the sql record's column
            yield nameValues


    def fieldsInObject(self):

        objlist = [obj for obj in self.context.values()]
#        oneLang = self.context.values(0)
        sqlRecordObject = removeSecurityProxy(objlist[0])
        # Note: there *must* be a better way of getting
        # just one value from the container.  Anyone have a
        # quick clue for me?

        fieldCount = 0
        # I imagine there is a better way of getting the
        # number of fields in a schema than that shown below.

        for iface in zope.interface.providedBy(sqlRecordObject):
            for name in iface:
                field = iface[name]
                if zope.schema.interfaces.IField.providedBy(field):
                    # We've got a schema field, now we can work with
                    # field.title, field.description, etc. (see IField)

                    # Since we get a 'bonus' item in our schema called __name__
                    # we must ignore it.  We use a list containment condition in
                    # case we want to expand the list of ignored attributes.
                    if field.__name__ in ['__name__']:
                        pass
                    else:
                        #print (field.__name__, sqlRecordObject.__dict__[field.__name__], )
                        fieldCount += 1

        return fieldCount

    def sqlItemCount(self):

        # Seems we have to use removeSecurityProxy
        # often to avoid ForbiddenAttribute errors
        return len(removeSecurityProxy(self.context))

==========================

I'd appreciate it if someone could please answer either
of the questions in the comments within the fieldsInObject method.

========= rowView.pt ============
<metal:block use-macro="context/@@standard_macros/view">
  <tal:block metal:fill-slot="body" define="rows view/rowView;
                                            colspan view/fieldsInObject;
                                            recordcount view/sqlItemCount">
    <table border="1">
    <tal:block repeat="row rows" i18n:domain="hello">
      <!-- the first row is a header showing a record count -->
      <tr tal:condition="repeat/row/start">
        <th colspan="8" tal:attributes="colspan colspan"
                        i18n:translate="record-count">
                        Number of items: <span i18n:name="recordcount"
                                               tal:omit-tag=""
                                               tal:content="recordcount">9</span>
        </th>
      </tr>
      <!-- the second row is a header showing field names -->
      <tr tal:condition="repeat/row/start">
        <tal:cellloop repeat="field row">
          <th tal:content="field/name">name</th>
        </tal:cellloop>
      </tr>
      <!-- The rest of the rows are data -->
      <tr>
      <tal:cellloop repeat="field row">
        <td tal:content="field/value">value</td>
      </tal:cellloop>
      </tr>
     </tal:block>
    </table>
  </tal:block>
</metal:block>
===============================================

The code is very much 'draft' quality and can stand
improvement, of course.  For example, robustness requires
more error checking, catching, and handling.  We should
probably sort in the database, and so on.

Regards,
Vinny




More information about the Zope3-users mailing list