Friday 9 November 2012

Manipulating zope.schema field's properties dynamically

Why do we want to do that in first place? Lets consider an use-case where we get loginid and user's name from an AddForm. But, in EditForm we don't want the user to change the login ID. One way to implement this, is to, omit the loginid field in the edit form's form_fields attribute. Suppose we want to display the loginid as read only field in EditForm alone, then we need to manipulate the schema field's default properties.

from zope import interface, schema

class IUser(interface.Interface):
    loginid = schema.BytesLine(title=u"Login ID", required=True)
    name = schema.TextLine(title=u"User name", required=True)


The zope.schema allows us to set a property named readonly to schema fields in the interface. For our case, if we specify them in the interface we can't be able to get the loginid from the user in AddForm. So, we are going to manipulate this at runtime in EditForm.

Let's see its implementation using Grok

import grok

class EditUser(grok.EditForm):
    grok.context(IUser)
    grok.name('edit')
    form_fields = grok.AutoFields(IUser)

    def setUpWidgets(self, ignore_request=False):
        # This method is responsible for constructing the widgets from
        # form_fields. So, we override this method to manipulate the
        # schema field
        self.form_fields['loginid'].field.readonly = True
        super(EditUser, self).setUpWidgets(ignore_request)
        # Once we change the field's properties, its effect will be
        # seen across forms. i.e even the AddForm after viewing an
        # EditForm will have the 'loginid' as readonly field. So, we
        # revert them back to their originals
        self.form_fields['loginid'].field.readonly = False

    @grok.action('Save')
    def edit(self, **data):
        self.applyData(self.context, **data)
        self.redirect(self.url(self.context.__parent__, 'list'))


No comments:

Post a Comment