Yay for unintended consequences

0 views
Skip to first unread message

qhar...@gmail.com

unread,
Apr 7, 2006, 12:32:29 AM4/7/06
to TurboGears
So, at the suggestion of several people on the list today, I figured
out the basics of using form widgets. So now my little toy learning
application (a wish list) is all widgety. Neat stuff. However, my
design for this program relies on the same sort of new/not new status
for the list items that the wiki20 tutorial uses to tell the "save"
class how to handle the form data. I can't for the life of me figure
out how to implement that sort of mechanism using the widgets. I tried
a ton of different things, and none of them work. Any guidance would be
appreciated.

-Regards-

-QH-

Kevin Dangoor

unread,
Apr 7, 2006, 7:33:01 AM4/7/06
to turbo...@googlegroups.com

That's actually a really good question. The first answer that comes
up: do it the way the 20 minute wiki *will* do it in the next
iteration: don't bother with a flag. Have the edit method catch
SQLObjectNotFound and add a record if that's the case.

The next answer is to use a FieldSet. What I'm about to say is heading
off into fuzzy territory for me, because I haven't played much with
FieldSets. (Michele and Alberto will undoubtedly correct me if I'm
wrong on this one). Let's say, in the wiki20 case, you've got a
FieldSet for the wiki page instance (we'll call that fieldset "page").
Your form would have that fieldset plus the hidden field. You'd pass
to your template a dictionary that looks like this:

dict(formdata=dict(page=page, new=new))

I'm not spelling out the widget stuff, since it sounds like you're
comfortably using widgets right now.

Kevin

qhar...@gmail.com

unread,
Apr 7, 2006, 11:29:50 AM4/7/06
to TurboGears
Thanks for the pointers, I'll play with that and see if I can get it
going the way I want. The only other "working" method I had come up
with anctually instantiated a database object before it was saved. That
got ugly when someone changed their mind and hit the back button in
their browser.

Another question has come up for me with regards to using form widgets.
I have seen that there are two basic layout types, table and list. How
does one do a more complex form layout using a widget? The real
application I need to build once I get more comfortable with TG has
some fairly complex forms that need to be laid out as compactly as
possible. A mockup of one of the pages is available here:
http://www.slane.k12.or.us/cmap_cedit.html . The main entry stuff on
the left would be simple enough, but getting the other entry box on the
right escapes me. Can one define custom layouts in one form like this
with widgets?

Jorge Godoy

unread,
Apr 7, 2006, 11:43:35 AM4/7/06
to turbo...@googlegroups.com
"qhar...@gmail.com" <qhar...@gmail.com> writes:

> Another question has come up for me with regards to using form widgets.
> I have seen that there are two basic layout types, table and list. How
> does one do a more complex form layout using a widget? The real
> application I need to build once I get more comfortable with TG has
> some fairly complex forms that need to be laid out as compactly as
> possible. A mockup of one of the pages is available here:

You have to write your own layout. The code in TableForm and ListForm can
help you getting started.

> http://www.slane.k12.or.us/cmap_cedit.html . The main entry stuff on
> the left would be simple enough, but getting the other entry box on the
> right escapes me. Can one define custom layouts in one form like this
> with widgets?

Yes, you can. It's in the class variable "template". Take a look at the
source -- it's not hard, it's inside turbogears/widgets/ and there you have
forms.py and other files -- and you'll get an idea.

--
Jorge Godoy <jgo...@gmail.com>

Michele Cella

unread,
Apr 8, 2006, 5:36:31 AM4/8/06
to TurboGears

As Kevin said, if what you need is passing values at display time to
your widgets that's what you can do.

Let's take the wiki20 Form:

<form action="save" method="post">
<input type="hidden" name="pagename" py:attrs="value=pagename"/>
<textarea name="data" py:content="data" rows="10" cols="60"/>
<input type="submit" name="submit" value="Save"/>
</form>

In Widgets language :D this becomes something like:

class MyFields(WidgetsList):
name = HiddenField()
data = TextArea()

wiki20_form = TableForm(name="page", fields=MyFields(),
action="save",
submit_text="Save")

Now if you problem is passing a value to the hidden field, that's what
you will do from your edit method (for example):

@turbogears.expose(html="wiki20.templates.edit")
def edit(self, pagename):
page = Page.byPagename(pagename)
form_values = dict(name=page.pagename, data=page.data)
return dict(form=wiki20_form, form_values=form_values)

in your edit.kid page you will need to do this then:

form.display(value=form_values)

One thing to keep in mind, if you're using a CompoundFormField (ATM a
FieldSet for example) it means you are using a nested field, the value
you get as input associated to such a field is not a plain value but a
dictionary containing its field, value pair.

For example:

class NestedFields(WidgetsList):
one = TextField()
two = TextField()

class MyFields(WidgetsList):
name = TextField()
nested = FieldSet(fields=NestedFields())

myform = TableForm(fields=MyFields(), action="save")

your save method needs to be something like this:

def save(self, name, nested):

The basic rule is that you just expect the parameter you've listed in
the WidgetsList class that you are passing as fields to the form
Widget, in this case fields=MyFields() so we use name and nested.

now when you submit such a form that's what you get as input:

name = "myname"
nested = {"one": "hey", "two": "hello"}

the same rule applies if you need to pass dynamically computed values
to your form for the first display, so in the previous example (wiki20)
you don't pass a flat dictionary but a nested dictionary like this one:

form_values = dict(name="michele",
nested=dict(one="yeah one",
two="really two"))

finally if you don't need to pass dynamically computed values of you
just want a field to always have a default value that should be used on
every first display of your form you can provide it at the widget
instantiation, for example:

name = TextField(default="Your name here!")

Ciao
Michele

lateef jackson

unread,
Apr 9, 2006, 2:18:21 PM4/9/06
to turbo...@googlegroups.com
Ok so I added a validator to my edit.
@turbogears.validate(form=myforms.COOL_FORM)
def cooledit(self,...):
  dict(editValues=..)

So the problem I am having is that if I use the validator decorator the form is not populated when it is first loaded. If I modify the data and submit it (submits to itself) it works correctly. If I comment out the validator decorator then I get the expected behavior of on first page visit the data is populated from the values I passed into the view from the controller. And it works fine submitting to itself. Is it frowned on to use widgets and submit to the same controller function? Or do we think there is a bug in my code?

Thanks,
lateef

Alberto Valverde

unread,
Apr 9, 2006, 2:34:20 PM4/9/06
to turbo...@googlegroups.com

On Apr 9, 2006, at 8:18 PM, lateef jackson wrote:

> Ok so I added a validator to my edit.
> @turbogears.validate(form=myforms.COOL_FORM)
> def cooledit(self,...):
> dict(editValues=..)
>
> So the problem I am having is that if I use the validator decorator
> the form is not populated when it is first loaded. If I modify the
> data and submit it (submits to itself) it works correctly. If I
> comment out the validator decorator then I get the expected
> behavior of on first page visit the data is populated from the
> values I passed into the view from the controller. And it works
> fine submitting to itself. Is it frowned on to use widgets and
> submit to the same controller function? Or do we think there is a
> bug in my code?

Have you tried passing a value to the form in the template?

def controllermethod(...)
...
return dict(form=form, value=dict(name="Haifa", surname="Wehbe"))

(in the template)

${form.display(value=value)}


Alberto.

lateef jackson

unread,
Apr 9, 2006, 9:37:43 PM4/9/06
to turbo...@googlegroups.com
Yeah that is how it works right now. I build the dictionary "value" in the controller then I pass that to the model and it get passed into the display method of the $form.
I am going to take a stab at converting the widgets docs to kid templates this week. I would like to add a non trivial example to the docs that submits a registration form to itself. It saves the data, and creates TG_Users (groups + permissions stuff). Anyway, I will try with a less complex use case and let you know if I have any problems.

Thanks,
Lateef

Sean Jamieson

unread,
Apr 10, 2006, 9:57:53 AM4/10/06
to TurboGears
While I was reading this topic, something just tickled my brain. I
remember that Kevin had mentioned somewhere (screencast probably) about
the posibility of widgets being defined as classes (ref. SQLObject),
rather than with an instantiated object, and a dictionary of fields.

I've been reading about metaclasses recently, and I think it would be
quite possible to create a meta class, that processes a class
definition, and produces something that returns the exact same
structure as currently used in widgets.

What do you think, insane or interesting?
Sean

Michele Cella

unread,
Apr 10, 2006, 10:18:19 AM4/10/06
to TurboGears

Sean Jamieson wrote:
> While I was reading this topic, something just tickled my brain. I
> remember that Kevin had mentioned somewhere (screencast probably) about
> the posibility of widgets being defined as classes (ref. SQLObject),
> rather than with an instantiated object, and a dictionary of fields.
>
> I've been reading about metaclasses recently, and I think it would be
> quite possible to create a meta class, that processes a class
> definition, and produces something that returns the exact same
> structure as currently used in widgets.
>
> What do you think, insane or interesting?
>

IMHO? insane... :D

That's not how an object is supposed to work, why should we use a class
instead of an instance? it's just not how OOP works and that's why you
can't do that without resorting to metaclass magic, I hope TG will
never do something similar because that could really be confusing and
feel strange to everyone.

But that's only my opinion. ;-)

Ciao
Michele

Alberto Valverde

unread,
Apr 10, 2006, 10:27:09 AM4/10/06
to turbo...@googlegroups.com

On Apr 10, 2006, at 3:57 PM, Sean Jamieson wrote:

>
> While I was reading this topic, something just tickled my brain. I
> remember that Kevin had mentioned somewhere (screencast probably)
> about
> the posibility of widgets being defined as classes (ref. SQLObject),
> rather than with an instantiated object, and a dictionary of fields.
>
> I've been reading about metaclasses recently, and I think it would be
> quite possible to create a meta class, that processes a class
> definition, and produces something that returns the exact same
> structure as currently used in widgets.
>
> What do you think, insane or interesting?


Insane :)

Metaclasses are very porwerful and solve some problems very
elegantly, but, like any other powerfull tool, should only be used
when absolutely needed (although it's fun to use them for uneeded
problems to get a grip of them, and have fun, of course).

I remember one case when I coded an internal app for the company I
work at to handle nightly backups (via rdiff-backup) of many servers.
Plugins were made to support different schemas (postgres, mysql,
tracs, svn repositories, etc...) and the idea was that these plugins
could be easily written by the python newbie coworkers. I decided to
take a shot on metaclasses so they didn't need to call super
cooperatively when extending methods. As you can guess, I ended up
reimplementing python's MRO in an unmaintainable mess and helped them
neither as when they wanted to write real python code they weren't
used the "super" idiom. But it's true that I learned a lot on the way...

The widget API right now is using metaclasses internally for various
stuff, but we really tried hard not to get overexcited with it
(thanks Michele for stopping my feet at #419 ;) as it would only over-
complicate things for no good reason.

Regards,

Alberto


Sean Jamieson

unread,
Apr 10, 2006, 10:39:31 AM4/10/06
to turbo...@googlegroups.com
Michele Cella wrote:

>IMHO? insane... :D
>
>That's not how an object is supposed to work, why should we use a class
>instead of an instance? it's just not how OOP works and that's why you
>can't do that without resorting to metaclass magic, I hope TG will
>never do something similar because that could really be confusing and
>feel strange to everyone.
>
>But that's only my opinion. ;-)
>
>Ciao
>Michele
>
>

heh, yeah, I thought it sounded a bit crazy, but I thought of that just
after waking up this morning.
But, crazy ideas about class/object mutation aside, I think it would be
preferable to define a form as a class. Besides the aesthetic difference
(which is a good one IMHO) it could also provide for better functionality.

i.e. you could define custom validation for your fields in the class,
I'm not sure I like this decorator method that exists now. But to be
honest, I haven't really used it (partly because of lack of docs).

pseudo example:
class MyForm( WidgetForm ):
Name = TextField(label=...[, class=..., value=..., size=..., ...])
Value = TextField(label=...[, class=..., value=..., size=..., ...])
Submit = SubmitButton(value=...[, name=..., class=...])

def _validateName( self, value ):
if len( value ) < 4:
return false
if not value.isalnum():
return false
...etc...

then defining a your method could simply be a matter of:

@expose()
@validate(MyForm, postOnly=true)
def save(self, **values):
# database code here


Anyway, that's my fantasy, I may make it my own reality eventually.

Sean

Alberto Valverde

unread,
Apr 10, 2006, 10:56:56 AM4/10/06
to turbo...@googlegroups.com

On Apr 10, 2006, at 4:39 PM, Sean Jamieson wrote:
> heh, yeah, I thought it sounded a bit crazy, but I thought of that
> just
> after waking up this morning.

Don't give up, crazy ideas sometimes turn out to be the best ones... :)

> pseudo example:
> class MyForm( WidgetForm ):
> Name = TextField(label=...[, class=..., value=..., size=..., ...])
> Value = TextField(label=...[, class=..., value=...,
> size=..., ...])
> Submit = SubmitButton(value=...[, name=..., class=...])
>
> def _validateName( self, value ):
> if len( value ) < 4:
> return false
> if not value.isalnum():
> return false
> ...etc...

You can use something smilar to de "declarative" style you're
proposing using WidgetList and the current API:

class NameValidator(FancyValidator):
def to_python(self, value, state=None):
# your validtion code here, raise Inavalid in not valid, return
coerced value

class MyFormFields(WidgetList):
name = TextField(validator=NameValidator())
value = TextField(...)
submit = submitButton(...) # You don't really need it as the form
provides...

class MyForm(Form):
fields = MyFormFields()
>

> Anyway, that's my fantasy, I may make it my own reality eventually.

well, I'd encourage you to share your fantasies (Widget related)
here, If they're good they will likely be part of TG for everyone to
enjoy.

Regards,
Alberto

ajones

unread,
Apr 10, 2006, 1:58:41 PM4/10/06
to TurboGears
Sean Jamieson wrote:
> While I was reading this topic, something just tickled my brain. I
> remember that Kevin had mentioned somewhere (screencast probably) about
> the posibility of widgets being defined as classes (ref. SQLObject),
> rather than with an instantiated object, and a dictionary of fields.
>
> I've been reading about metaclasses recently, and I think it would be
> quite possible to create a meta class, that processes a class
> definition, and produces something that returns the exact same
> structure as currently used in widgets.
>
> What do you think, insane or interesting?
> Sean
>
This seems pretty similar to what FastData already does. Granted I have
no idea if it uses metaclasses, but I know it creates an html form from
a SQLObject somehow. That might be a good place to start looking, see
if you could lift code from that.

Michele Cella

unread,
Apr 10, 2006, 2:56:46 PM4/10/06
to TurboGears
ajones wrote:
>
> This seems pretty similar to what FastData already does. Granted I have
> no idea if it uses metaclasses, but I know it creates an html form from
> a SQLObject somehow. That might be a good place to start looking, see
> if you could lift code from that.

FastData simply uses formmaker.fields_for to ispect the SQLObject model
and build a list of widgets that are then passed to the Form
constructor as the fields argument, the other somewhat special thing is
that the form parameter passed to the validate decorator is not a form
instance but a callable that returns the above form instance. ;-)

Ciao
Michele

Reply all
Reply to author
Forward
0 new messages