On 2/20/06, David Johnson <[EMAIL PROTECTED]> wrote: > I've implemented the browser:form ZCML directive. The implementation works > well until I add an __init__ method to my view class. The following error > is generated. > File > "/home/myuser/Zope-3.2.0/build/lib.linux-i686-2.4/zope/app/form/browser/editview.py", > line 76, in widgets > return [getattr(self, name+'_widget') > AttributeError: 'SimpleViewClass from edit.pt' object has no attribute > 'name_widget' > > Here is the __init__ that leads to the error > ---: > > class Function(object): > """A transaction function.""" > __used_for__ = ITPM > def __init__(self, context, request, base_url=''): > self.context = context > self.request = request > self.base_url = base_url
First - if you're running on Zope 3.2, I (and many others) would recommend looking at zope.formlib. Later in this message, I go into a little more detail about how it might help you. But the direct answer is - you need to call ``super(...)``. "Use super()?" you ask, "I'm just subclassing from ``object``, right?" Wrong! The browser:form directive, like many view generating directives for ZCML, is making a new class on the fly by mixing in a few base classes and your class together. Those other classes that are used as bases are likely to provide their own __init__, and you need to ensure they get called because they could be doing something important, like one of the zope.app.form.browser form classes is doing in this case. Try this: class Function(object): .... def __init__(self, context, request, base_url=''): self.base_url = base_url super(Function, self).__init__(context, request) That will set up context and request for you. More importantly, it *should* call __init__ on the class that's created by the <browser:form> directive, which calls something like self.setUpWidgets. self.setUpWidgets (I'm not sure that's the method name, but it's something like that) creates attributes on the view in the form of ``fieldname_widget``: so ``name_widget``, ``title_widget``, and so on, based on the schema and options provided by ZCML. Since you're not calling super(), that setup step is not being run. But I have a question: how is this form going to be used? Are you instantiating it directly in other Python code? Because it's unlikely that base_url will ever have a value applied to it otherwise. Most views are instantiated in Zope by calling ``getMultiAdapter((context, request)...)``, which calls __init__ on the view object found and passes in context and request as the two arguments. Unless you are explicitly calling ``Function(context, request, 'foo/bar')``, that third argument is unlikely to be set. If you just want to ensure that it's there as an attribute, you can put it in the class definition:: class Function(object): base_url = "" And then you don't have to override __init__. It's probably not a good idea to provide/expect extra __init__ arguments when writing an adapter (or view), since most of the time it's the adapter lookup machinery that will be calling that __init__ code. It's not bad to write your own __init__ though. But I do recommend trying to find out what classes you're inheriting from and what they do in __init__ so you can either do it yourself or use super(). Formlib ======= If you're using forms and you're running Zope 3.2, look at zope.formlib. With formlib, it's easier to see what classes you can inherit from and what they're doing and when you might want to override a method and would need to call super, and what you would need to pass along to the superclass when you do. This is an example of what that might look like: from zope.formlib import form class Function(form.FormBase): # form.Fields finds the fields that might have been provided by # browser:form schema="...ITPM". form_fields = form.Fields(ITPM) base_url = '' # if you have a custom template, you can provide it here # instead of ZCML template = ViewPageTemplateFile('functionform.pt') And then register via <browser:view>. (You'd need to do a little bit more work, of course, to load/bind custom data or respond to a 'Save' action, depending on your requirements). It really is a more flexible, adaptive, controllable, and documented system than the form views available from zope.app.form via the <browser:*form> directives. You should be able to print out the documentation from apidoc in the 'book' menu, and look up the IFormAPI interface too. All of our custom forms became so much easier to control and understand after moving to formlib, and in many cases the code to support a form actually shrank. There's a lot more plain old Python code and a lot less ZCML magic, which makes it easier to trace how a form and its widgets get built. It might take a little bit of time to read and understand all of the options available, but it's well worth it as I've found zope.formlib to be a wonderfully solid foundation to build on. -- Jeff Shell _______________________________________________ Zope3-users mailing list Zope3-users@zope.org http://mail.zope.org/mailman/listinfo/zope3-users