On Mon, Jan 22, 2007 at 05:03:43PM +0100, Alex Furlong wrote:
> i have some problems with creating a custom edit form. Here is my complete 
> test code:
> from zope.formlib import form
> from zope.publisher.browser import TestRequest
> class MyEdit(form.EditForm):
>     def __init__(self, context, request):
>         self.context = context
>         self.request = request
>         import pdb;pdb.set_trace() # set debugger break point
>         action = form.Action('Edit', success='handle_edit_action')

Ah, this is going to be fun :)

>         self.actions = form.Actions(action)
> def test():
>     request = TestRequest()
>     obj = object()
>     myform = MyEdit(obj, request)
> If i use this code in Zope then i get an error like this: "Action
> object does not have an attribute 'form' ".


> So my first question is, where does the 'form' attribute come from?


Do you know how methods work in Python?  You assign a function to a
class attribute, and when you access that attribute on an instance of
the class, you get back a bound method.  The bound method knows which
instance you used -- that's why it's called a bound method.

Under the hood this is implemented using descriptors.  A descriptor is
an object that has a special __get__ method.  When the value of a class
attribute is a descriptor, and you access that attribute on an instance,
the descriptor's __get__ gets called and return the value that you'll

The __get__ of an unbound method will create a bound method object.
Likewise, the formlib's form.Action class has a __get__ method that
returns a copy of the action, with the 'form' attribute set.

This mechanism only works when you assign descriptors to class
attributes.  It does not work when you assign descriptors to object
attributes, or local variables, like you did.

The simplest way to fix your problem is to bind the actions manually:

      def __init__(self, context, request):
          self.context = context
          self.request = request
          action = form.Action('Edit', success='handle_edit_action')
          action = action.__get__(self)
          self.actions = form.Actions(action)

Or you can do it all in one go

      def __init__(self, context, request):
          self.context = context
          self.request = request
          action1 = form.Action('Edit', success='handle_edit_action')
          action2 = form.Action('Edit Again', success='handle_edit_action')
          actions = form.Actions(action1, action2)
          self.actions = form.Actions(actions).__get__(self)

I do not know whether there are cleaner solutions.  (I always feel a bit
dirty when I call a method that starts with an underscore on an object
other than 'self'.)

Marius Gedminas
TCP_UP - The 16-bit TCP Urgent Pointer, encoded as the hex
      representation of the value of the field.  The hex string MUST be
      capitalized since it is urgent.
                -- RFC 3093

Attachment: signature.asc
Description: Digital signature

Zope3-users mailing list

Reply via email to