[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@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users

_______________________________________________
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users

_______________________________________________
Zope3-users mailing list
Zope3-users@zope.org
http://mail.zope.org/mailman/listinfo/zope3-users

Reply via email to