简体   繁体   中英

Flask-WTForms RadioField custom validator message doesn't work

In Flask-WTForms , we can give a custom message for each validator for each field. But for RadioField it shows the default message only. Below is an example.

>>> from wtforms import Form, RadioField, TextField
>>> from wtforms.validators import *

TextField

>>> class MyForm(Form):
        x = TextField(u'Some text', validators = [Required(message="Hello")])

Error Message

>>> form = MyForm()
>>> form.x.data
>>> form.validate()
False
>>> form.errors
{'x': ['Hello']}

So for a TextField it shows the custom error message.

RadioField

>>> class MyForm(Form):
        x = RadioField(choices = [(1, '1'), (2, '2')], validators = [Required(message="Hello")])

Error Message

>>> form = MyForm()
>>> form.x.data
u'None'
>>> form.validate()
False
>>> form.errors
{'x': [u'Not a valid choice']}

The custom error message is not there. I guess, validation for TextField and RadioField will be different process and may be that's why it's showing the default message.

So my Question is How to show a custom message for the validation of a RadioField ?

You are right about that the process is different.

So if you go do source code there is base Field class with validate method. It's said

 """ Validates the field and returns True or False. `self.errors` will contain any errors raised during validation. This is usually only called by `Form.validate`. Subfields shouldn't override this, but rather override either `pre_validate`, `post_validate` or both, depending on needs.> :param form: The form the field belongs to. :param extra_validators: A sequence of extra validators to run. """ 

And the procedure of validation is pre_validate() -> validate() -> post_validate() (Call pre_validate -> Run validators -> Call post_validate )

As you can guess, RadioField has it's own pre_validate() method, but basically SelectField 's one. And then when RadioField is inherit from SelectField it has it too.

 def pre_validate(self, form): for v, _ in self.choices: if self.data == v: break else: raise ValueError(self.gettext('Not a valid choice')) 

So that's why you get 'Not a valid choice' error instead of your custom one, on wtforms.validators.Required() validator, because it just didn't go through the pre_validate() and it stops.

Note : Required validator is depracated and will be removed in WTForms 3.0, and in that pull request they already removed it's usage. Instead of Required() validator, use DataRequired()

UPDATE : Since you add your validator to field you still should be able to get your error. Because since pre_validate() raises only ValueError it doesn't stop validate()

 # Call pre_validate try: self.pre_validate(form) except StopValidation as e: if e.args and e.args[0]: self.errors.append(e.args[0]) stop_validation = True except ValueError as e: self.errors.append(e.args[0]) 

And then it goes to

 # Run validators if not stop_validation: chain = itertools.chain(self.validators, extra_validators) stop_validation = self._run_validation_chain(form, chain) 

And here is your validator exists and should add new error to the list of errors( form.x.error ), however it converts None to 'None' , so your form.x.data becomes 'None' ( str type), and now it goes to __call__

 def __call__(self, form, field): if not field.data or isinstance(field.data, string_types) and not field.data.strip(): if self.message is None: message = field.gettext('This field is required.') else: message = self.message field.errors[:] = [] raise StopValidation(message) 

And the condition not field.data is False , because field.data is 'None' . Why data convert from None to 'None' for SelectField and its related ones is explained in Issue on GitHub and probably be fixed when Pull Request will be merged in master.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM