TG-0.9-a2: Forms and widgets headaches

1 view
Skip to first unread message

Andrey Lebedev

unread,
Apr 2, 2006, 8:39:18 AM4/2/06
to TurboGears
Hello, group, excuse me for the long post, I hope clearing out the
topics below will be very helpful for TurboGears.

I am playing with turbogears-0.9-a2, specifically with forms and
widgets and I had/have a few headaches with them. I am new to TG
and I understand that the code is still alpha, but still I think we
can discuss the issues here.

I've found some docs on forms here:
http://trac.turbogears.org/turbogears/wiki/SimpleWidgetForm

and started playing with forms using this simple code:
http://trac.turbogears.org/turbogears/wiki/FormValidationWithWidgetsTwo

(The code displayed a form with two password fields, that had to
match to proceed). It worked as expected. The problems stated when I
tried to improve it a bit.


PROBLEM I. Magic in method calls discovered.

First thing I wanted is to show error message when passwords didn't
match. So I added argument to index() method (as described in
docs)::

def index(self, tg_errors=None):
turbogears.flash(tg_errors)
...

Flash message was always "None". I sarted investigation. I modified
index() signature:

def index(self, tg_errors=None, *args, **kw):
...

Such a modification doesn't cause problems in python, however when I
run a program, I've got "TypeError: index() got multiple values for
keyword argument 'tg_errors'". I removed *args argument and error
disappeared, but tg_errors was still always None. After a few hors
of tweaking the code and looking deep into the inner workings I left
only **kw argument of index() method and thats when I could access
form error information.

That's not really a pythonic behaviour, a true sign, that some magic
is going on behind the scenes and might be a little frustrating for
the newbies (even if such behaviour is documented somewhere).


PROBLEM II. Validator weirdness

Next thing I wanted to improve is to add a field to the form.
Naturally, I added some declarations, so my form and schema looked
like this::

class MySchema(validators.Schema):
pwd1 = validators.String()
pwd2 = validators.String()
pwd3 = validators.String(not_empty=True , max=5)
chained_validators = [validators.FieldsMatch('pwd1', 'pwd2'), ]

def createPasswordForm(controller=None):
field1 = widgets.PasswordField('pwd1')
field2 = widgets.TextField('pwd2')
field3 = widgets.TextField('pwd3')
form = widgets.TableForm(fields=[field1, field2, field3],
name='myform', validator=MySchema())
return form


New field appeared in the form as expected, but it did not validated
at all. I am trying to make new pwd3 field required, but form gets
processed regardless of emptiness of that field. When a developer
encounters problem like this, he starts to look for a problems in
form declaration. However, the validation started to work only after
I added additional argument to test() method::

def test(self, pwd1, pwd2, pwd3=None):
...

Now, after I discovered such behaviour, I _could_ explain to myself,
that if the function ignores a form field, it doesn't matter if that
field validated or not, but such a knowledge costed me some time
and, again IMHO, could confuse new users.


PROBLEM III. Preserving values in failed form.

That's the problem I haven't found a clean solution for. When, for
example, user forgets to fill out required field and submits the
form, generally form is redisplayed and error message is displayed
to the user. All filled fields in this case should stay filled as
they were before submit (even fields with errors). The user should
only correct bad fields and try submitting again.

Turbogears manual (I know, alpha, but nevertheless) talks only about
add forms (where forms initially empty) and doesn't address this
problems. The method of pre-filling forms is described there, but
that's not a solution too. To address the problem widgets should
search for value to display in this order:

1. Raw values from http request.
2. Values, supplied by developer (by pre-filling the form).
3. Fall back to empty value.

In this case, forms will preserve their values after submit as
expected by user.


P.S. HTTP forms are HARD. I haven't seen a single forms framework,
that one could call a "Forms on Rails" or "TurboForms" :). They are
usually are too simple (and therefore hard to use in cases beyond
the tutorial) or too complicated (and therefore hard to use in all
cases). OTOH forms are must have feature to every web framework. I
think TG has a good potential here and I really wish it to success.
Keep up the great work! :)

Simon Belak

unread,
Apr 2, 2006, 1:28:47 PM4/2/06
to turbo...@googlegroups.com
Andrey Lebedev wrote:
> PROBLEM I. Magic in method calls discovered.
>
> First thing I wanted is to show error message when passwords didn't
> match. So I added argument to index() method (as described in
> docs)::
>
> def index(self, tg_errors=None):
> turbogears.flash(tg_errors)
> ...
>
> Flash message was always "None". I sarted investigation. I modified
> index() signature:
>
> def index(self, tg_errors=None, *args, **kw):
> ...
>
> Such a modification doesn't cause problems in python, however when I
> run a program, I've got "TypeError: index() got multiple values for
> keyword argument 'tg_errors'". I removed *args argument and error
> disappeared, but tg_errors was still always None. After a few hors
> of tweaking the code and looking deep into the inner workings I left
> only **kw argument of index() method and thats when I could access
> form error information.
>
> That's not really a pythonic behaviour, a true sign, that some magic
> is going on behind the scenes and might be a little frustrating for
> the newbies (even if such behaviour is documented somewhere).

While there is some magic, it should be so magical it appearers mundane. ;)

Would you mind posting all of the code, as it seems you have stumbled
upon a bug.

Thanks!

Cheers,
Simon

Andrey Lebedev

unread,
Apr 2, 2006, 4:39:40 PM4/2/06
to TurboGears
OK, that's all the code, demonstrating the problem. It is not much
different from the original:

import cherrypy
import turbogears
from turbogears import controllers
from turbogears import identity
from turbogears import widgets
from turbogears import validators

class MySchema(validators.Schema):
pwd1 = validators.Int()
pwd2 = validators.Int()


pwd3 = validators.String(not_empty=True , max=5)
chained_validators = [validators.FieldsMatch('pwd1', 'pwd2'), ]

def createPasswordForm(controller=None):
field1 = widgets.PasswordField('pwd1')
field2 = widgets.TextField('pwd2')
field3 = widgets.TextField('pwd3')
form = widgets.TableForm(fields=[field1, field2, field3],
name='myform', validator=MySchema())
return form

class FormTest(controllers.Controller):
@turbogears.expose(template="turbogears.fastdata.templates.form")
def index(self, tg_errors=None, **kw):
turbogears.flash(tg_errors)
return dict(obj=None, action='test', form=createPasswordForm())

@turbogears.expose()
@turbogears.error_handler(index)
@turbogears.validate(form=createPasswordForm)


def test(self, pwd1, pwd2, pwd3=None):

return "Password1: %s<br />Password2: %s" % (pwd1, pwd2)

Simon Belak

unread,
Apr 3, 2006, 5:31:38 AM4/3/06
to turbo...@googlegroups.com
Fixed in [1068]. Thanks!

Cheers,
Simon

Andrey Lebedev

unread,
Apr 3, 2006, 5:47:35 AM4/3/06
to turbo...@googlegroups.com
On 4/3/06, Simon Belak <simon...@hruska.si> wrote:
>
> Fixed in [1068]. Thanks!

Thanks for fixing that! Any thoughts about other two problems?
(original post:
http://groups.google.com/group/turbogears/browse_thread/thread/90dafc48ceac3f71/790796d289f9eb6c?tvc=2)

--
Andrey Lebedev
Software engineer

Sean Jamieson

unread,
Apr 4, 2006, 12:56:07 PM4/4/06
to TurboGears
> "TypeError: index() got multiple values for keyword argument 'tg_errors'"

I've gotten this error in a couple of cases myself, I think it's a
problem with the CherryPy beta that's being used with TG 0.9x, I'm not
sure exactly what's happening, but it seems that using the new
positional arguments, and having *args, aren't playing well together.

Alberto

unread,
Apr 5, 2006, 4:03:39 PM4/5/06
to TurboGears
Hi,

I've commented something on this @
http://trac.turbogears.org/turbogears/ticket/721
Sorry for not looking at it before but I haven't been following this ML
too closely lately and I've missed this post.

Alberto

Reply all
Reply to author
Forward
0 new messages