I am glad to give you code examples (some follow at the end of this
message), but I may not have stated the problem as clearly as
possible.  I am concerned with how the SelectionField *displays* its
data, not how it *validates* incoming data.  (Validation works just
fine).  I provide the SingleSelectField with a Python value to be
*displayed*, and it (should, I believe) either

a) Convert that python value to a string using from_python and compare
it with the <option>s to see which one is selected, OR
b) Convert each <option> value to a corresponding Python value using
to_python to see which one is selected.

What it actually *does*, however, is

c) Try to convert the *python value* to another *python value* using
to_python and compare it with the <option>s to see which one is
selected (obviously never matching anything)

Before I get into my specific code, here's a quick contrived example
of how it currently works and how I think it should work:

    @expose(template='.foo')
    def test(self):
        class TestObj(object):
            def __init__(self):
                self.testval='three:3'

        class TestValidator:
            def to_python(self, value, state=None):
                log.info('Call to_python on value "%s"', value)
                result = {'1':'one:1',
                          '2':'two:2',
                          '3':'three:3',
                          '4':'four:4',
                          '5':'five:5' }.get(value, None)
                if result is None: raise Invalid('Bad Value', value,
state)
                return result
            def from_python(self, value, state=None):
                log.info('Call from_python on value "%s"', value)
                name, id = value.split(':')
                return id

        class TestFields(WidgetsList):
            testval=SingleSelectField(
                'testval', label='Test Value',
                options=[('1','one'), ('2','two'), ('3','three'),
('4','four')],
                validator=TestValidator())

        test_form = TableForm('test', fields=TestFields())

        return dict(form=test_form,
                    item=TestObj())

The template has the expected ${form.display(item)}.  If I use the
widgets code as-is, I get the following log output:

...
2007-03-04 07:00:58,853 tutornet.controllers INFO Call to_python on
value "three:3"
2007-03-04 07:00:58,982 tutornet.controllers INFO Call to_python on
value "three:3"
2007-03-04 07:00:58,982 tutornet.controllers INFO Call to_python on
value "three:3"
2007-03-04 07:00:58,983 tutornet.controllers INFO Call to_python on
value "three:3"

And of course, no option in the select field is ever selected.  If I
switch the widgets code to call from_python, however, I get the
following log output:

...
2007-03-04 07:04:34,820 tutornet.controllers INFO Call from_python on
value "three:3"
2007-03-04 07:04:34,822 tutornet.controllers INFO Call from_python on
value "three:3"
2007-03-04 07:04:34,823 tutornet.controllers INFO Call from_python on
value "three:3"
2007-03-04 07:04:34,825 tutornet.controllers INFO Call from_python on
value "three:3"

And the third option is correctly selected.

Now in my *actual* code, I have a validator which converts incoming
ids into objects using the SA .get method:

class SQL(FancyValidator):
    not_empty=True
    messages = dict(
        integer='Please enter an integer value',
        notFound='The %(model)s with ID %(value)s was not found')
    def __init__(self, model, method='get', base=Int, *l, **kw):
        self.model = model
        self.method = method
        self.base = base
        self.l = l
        self.kw = kw
        self._bound = False
        if isinstance(model, str):
            ##print 'Delay binding of SQL validator on %s' % model
            pass
        else:
            self._bind()

    def _bind(self):
        if isinstance(self.model, str):
            self.model = classregistry.findClass(self.model)
        self.method = getattr(self.model, self.method)
        if 'if_empty' in self.kw:
            self.not_empty=False
        self.base = self.base(*self.l, **self.kw)
        self._bound = True

    def _to_python(self, value, state):
        if not self._bound:
            self._bind()
        value = self.base.to_python(value)
        try:
            return self.method(value)
        except SQLObjectNotFound:
            raise Invalid(self.message('notFound', state,
                                       model=self.model.__name__,
                                       value=value),
                          value, state)

    def _from_python(self, value, state):
        if isinstance(value, int): keyval = value
        else: keyval = value.id
        return Int().from_python(keyval, state)

This is used in a form as follows:

class CurriculumItemFields(WidgetsList):
    type=SingleSelectField(label='Item Type',
                           options=lambda: [(t.id, t.name) for t in
model.ItemType.select()],
                           validator=SQL(model.ItemType))
    name=TextField()
    description=TextArea(attrs=dict(rows=2))
    textile=TextArea(label='Content', attrs=dict(rows=10),
                     validator=UnicodeString(if_empty=''))
    attachment=FileField()

edit_curriculum_item_form=TableForm(
    'curriculum_item_form',
    fields=CurriculumItemFields(),
    action='./save',
    submit_text='Save Curriculum Item')

In my controller, I retrieve a CurriculumItem (which has a "type_id"
foreign key into ItemType's table) from the database and then send it
to the template as (simplified):

    return dict(item=model.CurriculumItem.get(id),
form=edit_curriculum_item_form)

When I attempt to do form.display(item) inside the Kid template,
however, it does not correctly set the "selected" attr on the correct
option (it does not set *any* "selected" attrs, in fact.)  This is
because it is trying to call SQL(model.ItemType).to_python(<ItemType
instance ...>) inside  _is_option_selected when rendering the widget.
Replacing the to_python with from_python works beautifully, correctly
coercing the value both in valididating the form and when rendering
it.

On Mar 3, 12:12 pm, "Diez B. Roggisch" <[EMAIL PROTECTED]>
wrote:
> Rick schrieb:
>
> > Hey all,
>
> > I was working with a SingleSelectField and a complicated validator and
> > noticed that, when the widget is rendering itself, it calls
> > _is_option_selected, which in turn calls the validator's "to_python".
> > It seems that "from_python" should actually be called, since we're
> > starting with a Python value and ending up with a form value to
> > compare against.  (Plus, it makes my code work when I switch them.)
> > I'm not sure if this should be created as a ticket/patch/whatever, or
> > if my understanding is just wrong, so if anyone could point me in the
> > right direction, it would be greatly appreciated.
>
> No, it shouldn't, as the validator will work on a request-parameter
> (always a string in the first place) but should result in something that
> is in the options list, e.g. an id from an object.
>
> If you show us some code, we might be able to spot why things don't work
> for you.
>
> 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
-~----------~----~----~----~------~----~------~--~---

Reply via email to