[Zope3-Users] want to use ObjectWidget and default add form

john saponara john at saponara.net
Sat Jan 5 00:20:52 EST 2008


[the subject has really become: how can a custom widget access content 
objects and containers?]

Hi,

I'm trying to model a 1-to-1 relationship in zope3: a Driver class that 
contains an instance of a Car class.  I model the car as a schema.Object 
inside the driver interface:

class IDriver(Interface):
    car = Object(
      title = u'car',
      default = None,
      schema=ICar,
      required = False)

and it seems that I need to define a custom widget in order to avoid a 
ComponentLookupError.  I'd like to let the user select from all the cars 
in the application's container, and I can provide that list in the form 
class:

from zope.formlib import form
class DriverEdit(form.EditForm):
    def cars(self):
       parent=zapi.getParent(self.context)
       return [name for name,child in parent.items() if 
ICar.providedBy(child)]
    form_fields = form.Fields(IDriver)
    form_fields["car"].custom_widget = CarsListWidget

but I don't know how to access the form instance from the widget class:

from zope.app.form.browser.widget import SimpleInputWidget
class CarsListWidget(SimpleInputWidget):
    def __call__(self):
       return '\n'.join([
          '<select name="%s" class="option-list">' % ('car'),
          '\n'.join(['\t<option>%s</option>' % (car)
             ### self.context is Object field, not DriverEdit form
             ###    so "self.context.cars()" FAILS
             for car in self.context.cars()]),
          '</select>',
          ])

I dont really need to access the form instance, but that seems to be a 
reasonable way to access the application's container (all drivers and 
cars are put into the same 'LimoService' container for now).

So how can I access the list of cars from the widget?  And does the 
answer differ in add forms vs edit forms?

Thanks!


john saponara wrote:
> thanks christophe.  while looking for an example of how to use 
> CustomWidgetFactory, i found a comment on the web about formlib 
> replacing browser:editform/addform (at 
> http://faassen.n--tree.net/blog/view/weblog/2005/09/06/0) and so i'm now 
> using the instructions in zope/formlib/form.txt.  unfortunately i still 
> had to customize the car widget to avoid an error.  for the car widget, 
> i want to let the user select from the list of all car objects in the 
> parent 'limoService' container, but when i do i get the error "Not 
> enough context information to get parent" at the getParent call.  my 
> custom widget is:
> 
> from zope.app.form.browser.widget import SimpleInputWidget
> class CarsListWidget(SimpleInputWidget):
>    def cars(self):
>       parent=zapi.getParent(self.context)
>       return [name for name,child in parent.items() if 
> ICar.providedBy(child)]
>    def __call__(self):
>       return '\n'.join([
>          '<select name="%s" class="option-list">' % ('car'),
>          '\n'.join(['\t<option>%s</option>' % (car) for car in 
> self.cars()]),
>          '</select>',
>          ])
> 
> and the full error (when trying to edit driver 'd1' via 'd1/edit.html') is:
> 
> 2008-01-03T21:56:05 ERROR SiteError 
> http://localhost:2020/mylimo/d1/edit.html
> Traceback (most recent call last):
>   File "C:\Python24\Lib\site-packages\zope\publisher\publish.py", line 
> 133, in publish
>     result = publication.callObject(request, obj)
>   File 
> "C:\Python24\Lib\site-packages\zope\app\publication\zopepublication.py", 
> line 161, in callObject
>     return mapply(ob, request.getPositionalArguments(), request)
>   File "C:\Python24\Lib\site-packages\zope\publisher\publish.py", line 
> 108, in mapply
>     return debug_call(obj, args)
>    - __traceback_info__: <security proxied 
> zope.app.pagetemplate.simpleviewclass.SimpleViewClass from 
> C:\pr\z3\lib\python\limoService\edit.pt instance at 0x0210EE50>
>   File "C:\Python24\Lib\site-packages\zope\publisher\publish.py", line 
> 114, in debug_call
>     return obj(*args)
>   File "C:\Python24\Lib\site-packages\zope\formlib\form.py", line 770, 
> in __call__
>     return self.render()
>   File "C:\Python24\Lib\site-packages\zope\formlib\form.py", line 764, 
> in render
>     self.form_result = self.template()
>   File 
> "C:\Python24\Lib\site-packages\zope\app\pagetemplate\viewpagetemplatefile.py", 
> line 83, in __call__
>     return self.im_func(im_self, *args, **kw)
>   File 
> "C:\Python24\Lib\site-packages\zope\app\pagetemplate\viewpagetemplatefile.py", 
> line 51, in __call__
>     sourceAnnotations=getattr(debug_flags, 'sourceAnnotations', 0),
>   File 
> "C:\Python24\Lib\site-packages\zope\pagetemplate\pagetemplate.py", line 
> 117, in pt_render
>     strictinsert=0, sourceAnnotations=sourceAnnotations)()
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 271, in __call__
>     self.interpret(self.program)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 861, in do_defineMacro
>     self.interpret(macro)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 909, in do_extendMacro
>     definingName, extending)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 891, in do_useMacro
>     self.interpret(macro)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 536, in do_optTag_tal
>     self.do_optTag(stuff)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 521, in do_optTag
>     return self.no_tag(start, program)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 516, in no_tag
>     self.interpret(program)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 861, in do_defineMacro
>     self.interpret(macro)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 957, in do_defineSlot
>     self.interpret(block)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 949, in do_defineSlot
>     self.interpret(slot)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 861, in do_defineMacro
>     self.interpret(macro)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 861, in do_defineMacro
>     self.interpret(macro)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 957, in do_defineSlot
>     self.interpret(block)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 957, in do_defineSlot
>     self.interpret(block)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 861, in do_defineMacro
>     self.interpret(macro)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 957, in do_defineSlot
>     self.interpret(block)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 534, in do_optTag_tal
>     self.no_tag(stuff[-2], stuff[-1])
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 516, in no_tag
>     self.interpret(program)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 824, in do_loop_tal
>     self.interpret(block)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 534, in do_optTag_tal
>     self.no_tag(stuff[-2], stuff[-1])
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 516, in no_tag
>     self.interpret(program)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 861, in do_defineMacro
>     self.interpret(macro)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 861, in do_defineMacro
>     self.interpret(macro)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 346, in interpret
>     handlers[opcode](self, args)
>   File "C:\Python24\Lib\site-packages\zope\tal\talinterpreter.py", line 
> 745, in do_insertStructure_tal
>     structure = self.engine.evaluateStructure(expr)
>   File "C:\Python24\Lib\site-packages\zope\tales\tales.py", line 696, in 
> evaluate
>     return expression(self)
>    - C:\Python24\Lib\site-packages\zope\formlib\pageform.pt
>    - Line 128, Column 14
>    - Expression: <PathExpr standard:u'widget'>
>    - Names:
>       {'args': (),
>        'context': <limoService.classes.Driver object at 0x021F16D0>,
>        'default': <object object at 0x00A3C568>,
>        'loop': {},
>        'nothing': None,
>        'options': {},
>        'repeat': {},
>        'request': <zope.publisher.browser.BrowserRequest instance 
> URL=http://localhost:2020/mylimo/d1/edit.html>,
>        'template': 
> <zope.app.pagetemplate.viewpagetemplatefile.ViewPageTemplateFile object 
> at 0x020BF3F0>,
>        'usage': <zope.pagetemplate.pagetemplate.TemplateUsage object at 
> 0x020FA2B0>,
>        'view': <zope.app.pagetemplate.simpleviewclass.SimpleViewClass 
> from C:\pr\z3\lib\python\limoService\edit.pt object at 0x0210EE50>,
>        'views': <zope.app.pagetemplate.viewpagetemplatefile.ViewMapper 
> object at 0x020FAA30>}
>   File "C:\Python24\Lib\site-packages\zope\tales\expressions.py", line 
> 217, in __call__
>     return self._eval(econtext)
>   File "C:\Python24\Lib\site-packages\zope\tales\expressions.py", line 
> 211, in _eval
>     return ob()
>   File "C:\pr\z3\lib\python\limoService\classes.py", line 41, in __call__
>     return '\n'.join([
>   File "C:\pr\z3\lib\python\limoService\classes.py", line 37, in cars
>     parent=zapi.getParent(self.context)
>   File "C:\Python24\Lib\site-packages\zope\traversing\api.py", line 140, 
> in getParent
>     raise TypeError("Not enough context information to get parent", obj)
> TypeError: ('Not enough context information to get parent', 
> <zope.schema._field.Object object at 0x0210ECF0>)
> 127.0.0.1 - - [3/Jan/2008:21:56:05 -0400] "GET /mylimo/d1/edit.html 
> HTTP/1.1" 500 84 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; 
> rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11"
> 
> and my complete code is:
> 
> ############################
> # interfaces.py
> 
> from zope.interface import Interface
> from zope.schema import Field, Text, TextLine, Choice, Int, Bool, Date, 
> Datetime, Object
> from zope.schema.vocabulary import SimpleVocabulary
> 
> from zope.app.container.constraints import ContainerTypesConstraint
> from zope.app.container.constraints import ItemTypePrecondition
> from zope.app.container.interfaces import IContained, IContainer
> 
> class ICar(Interface):
> 
>    model = TextLine(
>       title = u'model',
>       description = u'model',
>       default = u'',
>       required = False)
>    nPassengers = Int(
>       title = u'nPassengers',
>       description = u'nPassengers',
>       default = 0,
>       required = False)
> 
> class IDriver(Interface):
> 
>    car = Object(
>       title = u'car',
>       description = u'car',
>       default = None,
>       schema=ICar,
>       required = False)
> 
> class ILimoservice(IContainer):
>    '''container for items of type ICar, IDriver'''
>    name = TextLine(
>       title=u'Limoservice',
>       description=u'a Limoservice container',
>       default=u'',
>       required=True)
>    def __setitem__(name, obj): pass
>    __setitem__.precondition = ItemTypePrecondition(ICar, IDriver)
> class ILimoserviceContained(IContained):
>    '''for types that can only be contained in a Limoservice'''
>    __parent__ = Field(constraint = ContainerTypesConstraint(ILimoservice))
> 
> ############################
> # classes.py
> 
> from zope.app import zapi
> from zope.interface import implements
> from zope.app.container.btree import BTreeContainer
> from zope.app.container.contained import Contained
> 
> from limoService.interfaces import ICar, IDriver, ILimoservice, 
> ILimoserviceContained
> 
> class Car(Contained):
>    implements(ICar,ILimoserviceContained)
>    model = u''
>    nPassengers = 0
> 
> class Driver(Contained):
>    implements(IDriver,ILimoserviceContained)
>    car = None
> 
> class Limoservice(BTreeContainer):
>    implements(ILimoservice)
>    name = "u''"
> 
> class CarView(object):
>     def message(self):
>         return '%s holds %d passengers' % (
>            self.context.model, self.context.nPassengers)
> class DriverView(object):
>    def message(self):
>       if self.context.car:
>          carInfo=self.context.car.__dict__
>       else:
>          carInfo=None
>       return 'driver drives car model %s' % (carInfo)
> 
> from zope.app.form.browser.widget import SimpleInputWidget
> class CarsListWidget(SimpleInputWidget):
>    def cars(self):
>       parent=zapi.getParent(self.context)
>       return [name
>          for name,child in parent.items() if ICar.providedBy(child)]
>    def __call__(self):
>       return '\n'.join([
>          '<select name="%s" class="option-list">' % ('car'),
>          '\n'.join(
>             ['\t<option>%s</option>' % (car)
>                for car in self.cars()]),
>          '</select>',
>          ])
> 
> from zope.formlib import form
> class CarEdit(form.EditForm):
>    form_fields = form.Fields(ICar)
> class DriverEdit(form.EditForm):
>    form_fields = form.Fields(IDriver)
>    form_fields["car"].custom_widget = CarsListWidget
> 
> ############################
> # configure.zcml
> 
> <configure
>     xmlns="http://namespaces.zope.org/zope"
>     xmlns:browser="http://namespaces.zope.org/browser"
>     i18n_domain="limoService"
>     >
>     
>   <interface
>       interface=".interfaces.ICar"
>       type="zope.app.content.interfaces.IContentType"
>       />
>   <interface
>       interface=".interfaces.IDriver"
>       type="zope.app.content.interfaces.IContentType"
>       />
> 
>   <class class=".classes.Car">
>     <implements
>         interface="zope.annotation.interfaces.IAttributeAnnotatable"
>         />
>     <factory
>         id="limoService.classes.Car"
>         description="a car"
>         />
>     <require
>         permission="zope.Public"
>         interface=".interfaces.ICar"
>         />
>     <require
>         permission="zope.ManageContent"
>         set_schema=".interfaces.ICar"
>         />
>   </class>
>   <class class=".classes.Driver">
>     <implements
>         interface="zope.annotation.interfaces.IAttributeAnnotatable"
>         />
>     <factory
>         id="limoService.classes.Driver"
>         description="a driver"
>         />
>     <require
>         permission="zope.Public"
>         interface=".interfaces.IDriver"
>         />
>     <require
>         permission="zope.ManageContent"
>         set_schema=".interfaces.IDriver"
>         />
>   </class>
> 
>   <browser:addMenuItem
>       class=".classes.Car"
>       title="a car"
>       permission="zope.ManageContent"
>       description='add Car'
>   />
>   <browser:addMenuItem
>       class=".classes.Driver"
>       title="a driver"
>       permission="zope.ManageContent"
>       description='add Driver'
>   />
> 
>   <browser:page
>       for=".classes.Car"
>       name="index.html"
>       class=".classes.CarView"
>       permission="zope.Public"
>       template='read.pt'
>       />
>   <browser:page
>       for=".classes.Driver"
>       name="index.html"
>       class=".classes.DriverView"
>       permission="zope.Public"
>       template='read.pt'
>       />
> 
>   <browser:page
>       for=".classes.Car"
>       name="edit.html"
>       class=".classes.CarEdit"
>       permission="zope.ManageContent"
>       template="edit.pt"
>       />
>   <browser:page
>       for=".classes.Driver"
>       name="edit.html"
>       class=".classes.DriverEdit"
>       permission="zope.ManageContent"
>       template="edit.pt"
>       />
> 
>   <interface
>       interface=".interfaces.ILimoservice"
>       type="zope.app.content.interfaces.IContentType"
>       />
> 
>   <class class=".classes.Limoservice">
>     <implements
>         interface="zope.app.annotation.interfaces.IAttributeAnnotatable"
>         />
>     <implements
>         interface="zope.app.container.interfaces.IContentContainer"
>         />
>     <factory
>         id="limoService.classes.Limoservice"
>         description="Limoservice"
>         />
>     <require
>         permission="zope.ManageContent"
>         interface=".interfaces.ILimoservice"
>         />
>     <require
>         permission="zope.ManageContent"
>         set_schema=".interfaces.ILimoservice"
>         />
>   </class>
> 
>   <browser:addMenuItem
>       class=".classes.Limoservice"
>       title="Limoservice"
>       permission="zope.ManageContent"
>   />
>   <browser:containerViews
>       for="limoService.interfaces.ILimoservice"
>       index="zope.View"
>       contents="zope.View"
>       add="zope.ManageContent"
>       />
> 
>   </configure>
> 
> ############################
> # read.pt
> 
> <html metal:use-macro="context/@@standard_macros/view">
> <body>
> <div metal:fill-slot="body">
>   <big tal:content="python: view.message()"></big>
> </div>
> </body>
> </html>
> 
> ############################
> # edit.pt
> 
> <html metal:use-macro="context/@@standard_macros/view">
> <body>
> <div metal:fill-slot="body" tal:content="view">
> </div>
> </body>
> </html>
> 
> 
> 
> Christophe Combelles wrote:
>> john saponara a écrit :
>>> hi,
>>>
>>> please point me to an example showing how to use ObjectWidget with a 
>>> default add form.  in case there is no example, perhaps my failing 
>>> attempt below could serve as one, once it's modified to work.
>>
>> [...]
>>
>>> ComponentLookupError: ((<zope.schema._field.Object object at 
>>> 0x03584090>, <zope.publisher.browser.BrowserRequest instance 
>>> URL=http://localhost:2020/mylimo/@@+/action.html>), <InterfaceClass 
>>> zope.app.form.interfaces.IInputWidget>, u'') 127.0.0.1 - - 
>>
>> It seems you have no widget associated to your Object field.
>> You should use less zcml and implement your own addform:
>>
>>   driver_widget = CustomWidgetFactory(ObjectWidget, Driver)
>>
>>   class DriverAddForm(AddForm):
>>     form_fields = Fields(IDriver)
>>     form_fields['car'].custom_widget = driver_widget
>>     def __init__(self, context, request):
>>         (...)
>>     def create(self.data):
>>         (...)
>>
>> Christophe
>> _______________________________________________
>> Zope3-users mailing list
>> Zope3-users at zope.org
>> http://mail.zope.org/mailman/listinfo/zope3-users
> 
> _______________________________________________
> Zope3-users mailing list
> Zope3-users at zope.org
> http://mail.zope.org/mailman/listinfo/zope3-users



More information about the Zope3-users mailing list