Use a ForEach validator. When a field derived from
MultipleSelectionMixin receives one, it will use it instead of
building using the one provided to validate individual values.
Something like this should work:
class AtLeastOne(ForEach):
validator = Int
def _to_python(self, value, state=None):
value = super(AtLeastOne, self)._to_python(value, state)
if len(value) < 1:
raise Invalid("at least one item must be selected",
value, state)
return value
(just committed a change to allow passing a ForEach (sub)class as
well as an instance)
Alberto
> class AtLeastOne(ForEach):
> validator = Int
> def _to_python(self, value, state=None):
> value = super(AtLeastOne, self)._to_python(value, state)
> if len(value) < 1:
> raise Invalid("at least one item must be selected",
> value, state)
> return value
>
> (just committed a change to allow passing a ForEach (sub)class as
> well as an instance)
>
I am lurking on this list, but I need to stick with the current TG
widgets for a while, and wonder if my solution can be applied to TW too:
> class AtLeastOne(FormValidator):
> def validate_python(self, form_value, state):
> if not form_value['fieldname']:
> raise Invalid('', None, state, error_dict = {'fieldname':
> 'at least one item must be selected'})
>
>
> class MySchema(Schema):
> ....
> chained_validators = [ AtLeastOne() ]
Yep. That validator should work without problems. In the example I
posted (extacted form the unittest) I override _to_python since
validate_python is not called when calling a validator's to_python
(which is what widget.validate delegates to). On the other hand, when
a Schema's to_python is called, it's subvalidator's validate_python
are called after the values are coerced.
Alberto
> Yep. That validator should work without problems. In the example I
> posted (extacted form the unittest) I override _to_python since
> validate_python is not called when calling a validator's to_python
> (which is what widget.validate delegates to). On the other hand, when
> a Schema's to_python is called, it's subvalidator's validate_python
> are called after the values are coerced.
>
Good. I also note you used super() in your overridden _to_python() method.
I thought metaclasses took care of handling/calling the superclasses'
methods?
btw,
I used to override to_python() and from_python() - without underscores -
by mistake, and I called super() everywhere.
Then I was quite puzzled to see I'm supposed to override _to_python()
and _from_python()
It's a bit inconsistent because there are no underscores in the other
methods that are usually overridden, and the docs are not clear enough.
>
> Alberto Valverde ha scritto:
>
>> Yep. That validator should work without problems. In the example I
>> posted (extacted form the unittest) I override _to_python since
>> validate_python is not called when calling a validator's to_python
>> (which is what widget.validate delegates to). On the other hand, when
>> a Schema's to_python is called, it's subvalidator's validate_python
>> are called after the values are coerced.
>>
>
> Good. I also note you used super() in your overridden _to_python()
> method.
>
> I thought metaclasses took care of handling/calling the superclasses'
> methods?
Hmm, I'm not sure but I don't think so... what makes you think they are?
>
>
> btw,
> I used to override to_python() and from_python() - without
> underscores -
> by mistake, and I called super() everywhere.
Happened to me too :) You should override the prefixed once or else
the validator won't behave correctly when those methods are used as
classmethods.... read on
> Then I was quite puzzled to see I'm supposed to override _to_python()
> and _from_python()
> It's a bit inconsistent because there are no underscores in the other
> methods that are usually overridden, and the docs are not clear
> enough.
This is because the black magic formencode.declarative.Declarative
does behind the scenes. It's related to the fact that:
Int.to_python()
and Int().to_python()
behave the same.
When you call them as classmethods, they'll delegate to a singleton's
instancemethod. This singleton is an instance that behaves like the
validator was instantiated with no arguments. OTOH, when you call
them on an instance they'll behave as a normal instancemethod. This
lets you use the class like if it was an instance when you want the
default behavior or instantiate it to override some arguments
(not_empty, strip, etc...). Wheter this conventient behavior deserves
the exra black magic is debatable... (who am I to say this when TW
does its good share of meta-magic too... ;)
If interested, take a look at formencode.declarative for more details.
Alberto
>> I thought metaclasses took care of handling/calling the superclasses'
>> methods?
>>
>
> Hmm, I'm not sure but I don't think so... what makes you think they are?
>
Nevermind, I tested it now, you're right, I was friday-confused.
> This is because the black magic formencode.declarative.Declarative
> does behind the scenes.
> [...]
> Wheter this conventient behavior deserves the exra black magic is debatable...
> (who am I to say this when TW does its good share of meta-magic too... ;)
>
I think it's worth it, altough such magic can hurt a bit at first,
expecially when it's not clearly explained in the docs.
> If interested, take a look at formencode.declarative for more details.
>
Sure, I might use the same pattern in my classes. Thank you.
> I'm using a CheckBoxList, and I'd like to perform a validation
> check "at least one item must be selected."
class AtLeastOne(ForEach):
validator = Int
def _to_python(self, value, state=None):
value = super(AtLeastOne, self)._to_python(value, state)
if len(value) < 1:
raise Invalid("at least one item must be selected",
value, state)
return value
Well, I've got my form validating pretty much as it should be. It was
easy enough to do with a validator on the form, although the error pops
up in the wrong place, so I have to jump through a couple of hoops to
display it neatly.
The code behind this is a bit of a mess at the moment. I guess as I
learn about toscawidgets I'll figure out how to tidy it up.
Thanks again,
Paul
Hi,
> I'm using a CheckBoxList, and I'd like to perform a validation
> check "at least one item must be selected."
class AtLeastOne(ForEach):
validator = Int
def _to_python(self, value, state=None):
value = super(AtLeastOne, self)._to_python(value, state)
if len(value) < 1:
raise Invalid("at least one item must be selected",
value, state)
return value
Thanks for the suggestion. Unfortunately, I can't get this to work. The CheckBoxList is inside a Form, and it seems that if a field is empty, the form's validator doesn't call that field's validator at all. If I change it to "at least two items must be selected", I correctly get the error if only one item is selected, but no items gives no error message.
> Hmmm, I believe this is related to the fact that browsers submit no
> vars for empty checkboxes and that TG widgets solve this by using the
> "if_missing" parameter to validators so they produce a value in this
> case. The Schema should detect this and call the subvalidator in this
> case (IIRC)
That makes sense. I wonder if because the value comes from "if_missing"
it decides it can bypass the validator? It definitely doesn't call the
validator as I would like it to. Still, this isn't a big issue for me;
I'm quite happy with the workaround.
I do have some observations on chained_validators:
1) It would be nice to display the errors from chained_validators within
the form being validated. This doesn't seem to be possible at the
moment; I've had to wrap the fields in an inner FieldSet, and display
the errors in the outer form. Works ok, but not ideal.
2) The errors from the chained_validator get mixed up with the errors
from the widgets in the form. In my case, if you enter an invalid date
in one of the fields, you get "invalid date" next to the date control,
and then where the form errors are displayed you get "start_date:
invalid date". I've got a workaround for now, but it would be good to
keep them separate.
3) If there are errors on the individual widgets, the chained validator
doesn't get called. This isn't a big deal, but it would be nice, so that
on a failed submission you report ALL the errors a user needs to correct.
I'm afraid I'm unlikely to get much chance to work on these in the near
future, but I hope you can get something in the plan.
All the best,
Paul
>
> Hi,
>
>> Hmmm, I believe this is related to the fact that browsers submit no
>> vars for empty checkboxes and that TG widgets solve this by using the
>> "if_missing" parameter to validators so they produce a value in this
>> case. The Schema should detect this and call the subvalidator in this
>> case (IIRC)
>
> That makes sense. I wonder if because the value comes from
> "if_missing"
> it decides it can bypass the validator? It definitely doesn't call the
> validator as I would like it to. Still, this isn't a big issue for me;
> I'm quite happy with the workaround.
I guess it's not calling from_python() since the value provided in
if_missing should be already a python value so there's no need to
coerce it.
>
> I do have some observations on chained_validators:
>
> 1) It would be nice to display the errors from chained_validators
> within
> the form being validated. This doesn't seem to be possible at the
> moment; I've had to wrap the fields in an inner FieldSet, and display
> the errors in the outer form. Works ok, but not ideal.
To display errors from chaine_validators beside their proper fields
you should populate the Invalid's error_dict attribute (you can pass
it when instantiating the exception) with each error at it's proper
key. OTOH, if you need to display an error for the whole form then
you should override the form's template to display its error.
Something like this should work:
<div py:if="error" py:content="error" />
>
> 2) The errors from the chained_validator get mixed up with the errors
> from the widgets in the form. In my case, if you enter an invalid date
> in one of the fields, you get "invalid date" next to the date control,
> and then where the form errors are displayed you get "start_date:
> invalid date". I've got a workaround for now, but it would be good to
> keep them separate.
This is normal if you did something like the above since the __str__
() method of Invalid's display all their subvalidators errors too.
This is how FE works and there's not much twForms can do about it
except choosing not to display the form's error.
>
> 3) If there are errors on the individual widgets, the chained
> validator
> doesn't get called. This isn't a big deal, but it would be nice, so
> that
> on a failed submission you report ALL the errors a user needs to
> correct.
IIRC, chained_validators are only called when pre_validators and the
subvalidators produce no errors. Again, this is FE's way of doing
things *which makes perfect sense IMO). To change this behavior maybe
a Schema subclass that overrides to_python() does the trick.
>
> I'm afraid I'm unlikely to get much chance to work on these in the
> near
> future, but I hope you can get something in the plan.
I think that to accomplish what you're asking for the only way would
be to customize FE's Schema, however, I'm trying to keep the standard
FE's behavior unchanged as much as I can so FE users will feel more
"at home" so I'm really not planning to do much about this (not
saying that twForms-FormEncode integration can't be improved...). I
believe the best solution would be to address this in a per-case
basis instead of forcing a non-standard behavior to all users.
Alberto