[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