[TransWarp] peak.web.forms (was re: State of the Onion)
Phillip J. Eby
pje at telecommunity.com
Sat Aug 9 17:32:28 EDT 2003
At 08:18 PM 8/9/03 +0200, Roché Compaan wrote:
>* Phillip J. Eby <pje at telecommunity.com> [2003-08-09 17:43]:
> > >During our
> > >discussion I thought that we don't really need "field" and that widgets
> > >can do all that you propose a field should do. I am still not convinced
> > >this is necessary separation, but I'll have to think about it some more.
> > Widgets shouldn't be mutable, but a widget class by itself will not
> > sufficient parameterization for its instances. Therefore, there are two
> > different objects at runtime: an object that describes the fixed
> > of the widget, and one that describes its state with respect to its
> > usage within the web hit.
>Why can't widgets manage state or why must they be immutable? They are
>not really loaded with services. In my mind they have to do very little:
>render a value and *maybe* store a value. In fact why do they have to
>keep any state at all - the values they need for rendering come from
>either the request or an object.
If they don't have state, then every method called has to have additional
parameters passed in, *and* there is a tighter coupling to the external
objects. If a widget looks at the request and the object, it is coupled to
both. Let's say we had a new data source we wanted to load a form from: to
do this would require changing every single widget.
Using the form-and-field approach, the form can have methods like
'loadFromRequest()' or 'loadFromObject()', or you can write external
functions that manipulate a form and its data. This is a greatly reduced
In general, I prefer to introduce more objects, and have each object
responsible for a distinct role. Such objects are usually much more
flexible, reusable, and maintainable. For example, the model I've
described so far could readily be adapted to a non-web GUI. One would
simply replace the "field" class with one that would also hold an actual
GUI widget, and would get/set the data from the GUI widget instance, rather
than keeping the data in itself. (Yes, of course there'd be more to such a
port than that, but it would still be better than having to rewrite all the
widgets so they don't expect to have a REQUEST object!)
In short, it's my "design sense" that asserts it be done this way. There
are many logical justifications, as you can see, but I don't actually think
about such "background" reasons for things when I design, I just "know" how
it should be, and can (sometimes) explain my reasons later. Such
horizontal concerns (things that apply to more than one kind of
application) are "background" thoughts for me.
> > >precisely because there may be differences between "modes". Sometimes
> > >you just want to remove widgets on the edit form that are visible on the
> > >add form. If the differences are overwhelming then create another form.
> > I'd prefer the objects to control their own destinies here. If there are
> > things that should be different, this should be specified by the form or
> > widget definitions, not by application code at runtime.
>If it is allowed to manipulate a form instance then I see no problem
Really, I don't want this to be the normal circumstance. Writing the last
email made it clear to me that forms want to drive the application, not the
other way around. E.g. an edit method should look more like:
def edit(self, CONTEXT):
form = self.editForm.forContext(CONTEXT, self.subject)
# render success template
# render previous template, using 'form'
And the above code should work perfectly whether editForm is a single
screen or multi-page form, whether it opens other windows to do searches,
or whether the object whose edit method this is, is being displayed as part
of a larger template.
And this construction as I've shown it is still too awkward, really. What
this should *really* do is have the *form* itself be the object posted to,
with this 'edit' being more like an 'on_Submit' operation.
Hmm. All this makes me want to take a look at XForms again, even though
XForms is designed for an XML data model on the back-end. Anyway, I'm
beginning to see that the high-level vision here is that you could actually
design the framework so that common GUI functions (add, edit, view) might
be accomplished without writing any app-level code at all; just create the
form design, and specify what it should do.
>with adding a widget to or removing a widget from a form instance at
>runtime. This would fall under changing their *state*, not?
Right, but this doesn't move us towards having full GUI separation.
>In PEAK however, I think we should code forms in python so that we can
>visualise them in UML and that is probably the page you are on already.
Well, I don't know about the UML bit; my "Web Extensions for UML" knowledge
is more than a little rusty. But definitely the framework should be able
to be driven by metadata, ala the "Naked Objects" approach. The dream here
is that if you should be able to go straight from a UML model to a default
(if perhaps ugly) GUI for it. If you could add a few stereotypes or tagged
values to the UML model to control initial widget selection, etc., however,
you might be able to very rapidly prototype new applications.
>I imagine you see an addForm as:
> class AddForm(Form):
> class Name(model.Attribute):
> referencedType = widget.TextWidget
> css_class = 'field_input'
> size = 20
> class Age(model.Attribute):
> referencedType = widget.TextWidget
Something like that, yes, but perhaps also very different. Up until now, I
hadn't really been thinking as much about using Python code to specify form
definitions, as I'd been focusing on very data-driven aspects. Now, I'm
realizing that you probably want some code that goes with this, maybe some
methods that manipulate form data in some way, or at least the option of
Ideally, it would be possible to have this code be limited, however, to
"form domain" matters, and not necessarily related to GUI matters, except
at an abstract level that would be applicable to multiple implementations
(e.g. wxPython vs. HTML).
Maybe I want too much. :) But I generally find it better to know what
direction I'd like to go, because I can always scale down the vision if the
budget doesn't allow it, but it's harder to change something that's already
built with a more limited scope in mind. Of course, this has to be
balanced against YAGNI-ism, but adaptation offers a powerful YAGNI weapon.
For example, suppose we made classes that corresponded to "abstract"
widgets, and then used adaptation to turn them into HTML widgets, wxPython
widgets, etc.? Not only that, but we could make the adaptation specific to
an individual form, using protocols.Variation. Anyway, one reason I want
to look at XForms again, is that they had a specification/model for a set
of abstract widgets that could then be mapped to specific widget
implementations using CSS and the like. I think that in our case we could
use pwt:define's in place of CSS.
An interesting thought - many GUI systems these days use XML as a
basis... besides HTML, there's also wxPython's XML resource format, and
Mozilla's XUL. All of these can be rendered with peak.web.templates, so
there do seem to be some intriguing possibilities for future GUIs.
>I think I should just make a prototype so that we have something
>concrete to work from. I find it difficult and frustrating to only talk
Feel free. I, on the other hand, don't know enough yet about what I want,
to make anything. From my POV, this is all still far-off brainstorming,
similar to where the existing peak.web stuff was a few months ago. What we
ended up with in developing the existing peak.web tools looks *very*
different from any of the things Ty and I first talked about, so I'm glad I
didn't rush to build any of the things we first talked about.
But, if you want a forms framework *now*, rather than possibly waiting a
few months, then rolling your own is indeed a good plan. Meanwhile, I've
been wondering when XForms might become important/relevant to
peak.web. Now that I've realized I'm trying to reinvent it, perhaps I
should just go see if we can port it instead. :)
More information about the PEAK