[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.

Michael Howitz mh at gocept.com
Tue Jan 19 14:47:23 EST 2010


Am 19.01.2010 um 16:58 schrieb Laurent Mignon:
> 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

This might be a solution.

>>> 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.

I see it differently. So I'd like to ask what was the original intention of ``getContent``? 
Anyone here to answer this question?

> 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)
> * ...

I might argue the other way round: when you override ``getContent`` in your form resp. group class you are on your own as you changed the default behavior of the form. Then you have to take care for for your groups to get the correct context.

> 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.

I tried it, it works, both in __init__ and in update. But this also works four your use case.

> 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

Yours sincerely,
-- 
Michael Howitz · mh at gocept.com · software developer
gocept gmbh & co. kg · forsterstraße 29 · 06112 halle (saale) · germany
http://gocept.com · tel +49 345 1229889 8 · fax +49 345 1229889 1
Zope and Plone consulting and development



More information about the Zope-Dev mailing list