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! :)
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
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)
Cheers,
Simon
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
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.
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