[Zope-dev] SVN: z3c.form/trunk/ ``GroupForm`` and ``Group`` now use ``getContent`` method when instantiating group classes instead of directly accessing ``self.context``, as this is the usual way to access the context of the form and allows nested groups to have a different context than the main form.

Laurent Mignon laurent.mignon at softwareag.com
Tue Jan 19 10:58:34 EST 2010


Michael Howitz wrote:
> The motivation is the following:
> 
> self.getContent() seems to be the pattern to access the context of a form. I used grep: there is no place where self.context is used directly besides in group.py. So it seems to be an error in the code not to use the common pattern.
> But after reading the doctests of z3c.form I'm no longer sure whether this is correct.
> 
> My use case is the following:
> 
> I have a folder with contained items. In a form I display the schema of the folder in the group form and the schemas of all contained items as groups. Each group has a nested group displaying the meta data for the group's content. As the context of the groups was the folder (group = groupClass(self.context, self.request, self)) the meta data displayed was wrong.
> 
But if your Groupform override getContent to provide an object 
implementing the expected interface, that's right

>> Class IAddress(Interface):
>>     street = zope.schema.TextLine(
>> 		title='street')
>>
>> Class IPerson(Interface);
>>     firstname = zope.schema.TextLine(
>>             	title='firstname')
>>
>>     address = zope.schema.Object(
>> 	        title='address',
>> 		schema = IAddress)
>>
>>
>> class Address(object):
>>     implements(IAddress)
>>
>>     def __init__(self, **kw):
>>         for name, value in kw.items():
>>  	    setattr(self, name, value)
>>
>>
>> class Person(object):
>>     implements(IPerson)
>>
>>     def __init__(self, **kw):
>>         for name, value in kw.items():
>>  	    setattr(self, name, value)
>>
>> class AddressGroup(group.Group):
>>     label = 'Address'
>>     fields = field.Fields(zope.schema.TextLine(
>>             __name__ = 'owner',
>>             title='Owner',
>>             readOnly=True)
>>     fields += field.Fields(IAddress).select('street')
>>
>>     def getContext(self):
> 
> Should be "getContent", I think.
> 
>>         return {
>>            'owner': self.context.firstname,
>>            'street': self.context.address.street}
>>
>> class PersonGroup(group.Group):
>>     label = 'Person'
>>     fields += field.Fields(IPerson).select('firstname')
>>
>>
>> class PersonEditForm(group.GroupForm, form.EditForm):
>>      fields = field.Fields(zope.schema.TextLine(
>>            __name__="description',
>>            title='Description',
>>            readOnly=True)
>>      groups = (PersonGroup, AddressGroup)
>>
>>      def getContent(self):
>>          return {'description': 'Form used to edit a person and its 
>> Address'}
> 
> I had never seen this before but according to the doctests of z3c.form it is a valid use case.

My understand of ``getContent`` is to provide a way to give values used 
by the widgets. By default, since the common use case is to edit / 
display values from the context, the implementation return the context.

If your form has to deal with fields defined in an interface not 
provided by the context, you have a lot of ways to provides the related 
values.
* The first one is to provide an adapter adapting the context to the 
interface defining the field
* The second one is to override the ``getContent`` implementation so 
it'll return an object implementing the right interface
* The thirds one is to override the ``getContent`` implementation so 
it'll return a dictionary where key = field.__name__ and value = the 
value expected by the field. (In fact a default adapter exist that adapt 
a dict to an interface)
* ...

Have a look to datamanager.txt....

>>
>> class MyEditForm(group.GroupForm, form.EditForm):
>>
>>     def __init__(self, context, request):
>>         super(MyEditForm, self).__init__(context, request)
>>         firstContext = {}
>>         secondContext = {}
>> 	self.groups = (
>> 	    MyFirstGroup(firstcontext, request, self),
>>             MySecondGroup(secondContext, request, self))
> 
> I'm not sure whether this works. (I'll try it.) Whether it works it obsoletes my changes.
> 

According to the z3c.form implemention, the right place to put your 
subform initialization is into the ``update`` method.

class MyEditForm(group.GroupForm, form.EditForm):

     def update(self):
	self.groups = (
  	    MyFirstGroup(self.context.obj1, request, self),
              MySecondGroup(self.context.obj2, request, self))
         super(MyEditForm,self).update()

Yours sincerely,

sagblmi




More information about the Zope-Dev mailing list