First off, thanks for the insights into the design philosophy of TG
widgets. OK, let me see if I can recap what you are saying. The
current implementation of _is_option_selected assumes that the "value"
in the options list [(value,desc,options)] is a Python value, but the
current implementation of the select field templates do not correctly
render that value into the template using the decorator's from_python
method, making it difficult to impossible to pass complex Python
values in the options list.
In that case, let me suggest that the SelectionField.update_params()
method be updated to actually use the validator's from_python method
to render the values into the template. I have included below a
version of the method which does just that. Unfortunately, this alone
does not fix the problem. Now, when update_params calls
is_option_selected, it is comparing two Python values (one from the
list, one from the value passed into the form). So in this case, it
seems that is_option_selected should skip the conversion step, since
it is comparing two Python values.
I have included monkey-patched versions of these two methods that seem
to give the desired behavior (passing Python values in the options
parameter, correctly rendering python values to the form using
from_python, and correctly determining which one is selected when
displaying/rendering the template). I've also included a fixed
version of my test controller method that works with these patches
fine. Do you see any problem with applying patches (real ones,
obviously, not these monkey-patches) to turbogears/widgets/forms.py?
Thanks,
Rick Copeland
=== Test case begin ===
@expose(template='foo')
def test(self):
class TestValue(object):
def __init__(self, value):
self.value = value
def __eq__(self, other):
# necessary to allow the SelectionField to determine
which value is selected
return self.value == other.value
class TestObj(object):
def __init__(self):
self.testval=TestValue(3)
class TestValidator:
def to_python(self, value, state=None):
log.info('Call to_python on value "%s"', value)
result = {'1':TestValue(1),
'2':TestValue(2),
'3':TestValue(3),
'4':TestValue(4),
'5':TestValue(5) }.get(value, None)
if result is None: raise Invalid('Bad Value', value,
state)
return result
def from_python(self, value, state=None):
return str(value.value)
class TestFields(WidgetsList):
testval=SingleSelectField(
'testval', label='Test Value',
options=[(TestValue(1),'one'), (TestValue(2),'two'),
(TestValue(3),'three'), (TestValue(4),
'four')],
validator=TestValidator())
test_form = TableForm('test', fields=TestFields())
return dict(form=test_form,
item=TestObj())
=== Test case end ===
=== Monkey Patches begin ===
def SF_update_params(self, d):
super(SelectionField, self).update_params(d)
grouped_options = []
options = []
d['options'] = self._extend_options(d['options'])
for optgroup in d["options"]:
if isinstance(optgroup[1], list):
group = True
optlist = optgroup[1]
else:
group = False
optlist = [optgroup]
for i, option in enumerate(optlist):
if len(option) is 2:
option_attrs = {}
elif len(option) is 3:
option_attrs = option[2]
if self._is_option_selected(option[0], d['value']):
option_attrs[self._selected_verb] =
self._selected_verb
optlist[i] = (self.validator.from_python(option[0]), #
This is the only line I changed
option[1], option_attrs)
options.extend(optlist)
if group:
grouped_options.append((optgroup[0], optlist))
# options provides a list of *flat* options leaving out any
eventual
# group, useful for backward compatibility and simpler widgets
d["options"] = options
if grouped_options:
d["grouped_options"] = grouped_options
else:
d["grouped_options"] = [(None, options)]
SelectionField.update_params = SF_update_params
def SF_is_option_selected(self, option_value, value):
# Removed validation logic here
if value is not None:
if self._multiple_selection:
if option_value in value:
return True
else:
if option_value == value:
return True
return False
SelectionField._is_option_selected = SF_is_option_selected
=== Monkey Patches end ===
On Mar 4, 6:06 pm, "Diez B. Roggisch" <[EMAIL PROTECTED]>
wrote:
> > No, it means that '1', '2', ... are the values that will be inserted
> > *without conversion* into the "value" attribute of the <option> tag in
> > the template.
>
> This is your misconception. These are the values that are used to be
> compared with the _to_python-converted request parameter value. You are
> right though that they aren't converted during rendering, except from
> being stringified.
>
> See the following example for a working pre-selection of a value in a
> SingleSelectField:
>
> ---- controller -----
> from turbogears import controllers, expose
> import turbogears.widgets as w
>
> import turbogears.validators as v
>
> def calc_options():
> return [(i, "value: %i" % i) for i in xrange(1000)]
>
> form = w.ListForm(fields=[w.SingleSelectField('test', options=calc_options,
>
> validator=v.Int(not_empty=True)),
> w.TextField('foo')])
>
> class Root(controllers.RootController):
> @expose(template="testproject.templates.welcome")
> def index(self):
> import time
> # log.debug("Happy TurboGears Controller Responding For Duty")
> return dict(now=time.ctime(), form=form, selected=100, foo='bar')
>
> ----- template ----
>
> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
> <html xmlns="http://www.w3.org/1999/xhtml"
> xmlns:py="http://purl.org/kid/ns#"
> py:extends="'master.kid'">
> <head>
> <meta content="text/html; charset=utf-8" http-equiv="Content-Type"
> py:replace="''"/>
> <title>Welcome to TurboGears</title>
> </head>
> <body>
> ${selected}
> ${form.display(dict(test='20', foo='baz'))}
> </body>
> </html>
>
> So, again: the values in the option-list are compared to whatever the
> validators _to_python-method yields.
>
> From the formencode docs:
>
> """
> The basic metaphor for validation is to_python and from_python. In this
> context "Python" is meant to refer to "here" -- the trusted application,
> your own Python objects. The "other" may be a web form, an external
> database, an XML-RPC request, or any data source that is not completely
> trusted or does not map directly to Python's object model. to_python is
> the process of taking external data and preparing it for internal use,
> from_python generally reverses this process (from_python is usually the
> less interesting of the pair, but provides some important features).
> """
>
> So - from that POV, it's the right way TG uses the validation stuff.
> OTH, it is sort of an omission that the current implementation doesn't
> use to from_python-method to convert e.g. model objects to id to render
> them as part of a select.
>
> Diez
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"TurboGears" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/turbogears?hl=en
-~----------~----~----~----~------~----~------~--~---