mirror of
https://github.com/wassname/wtforms-parsleyjs.git
synced 2026-07-15 11:28:41 +08:00
429 lines
12 KiB
Python
429 lines
12 KiB
Python
__author__ = 'Johannes Gehrs (jgehrs@gmail.com)'
|
|
|
|
import re
|
|
import copy
|
|
import json
|
|
|
|
from wtforms.validators import Length, NumberRange, Email, EqualTo, IPAddress, \
|
|
Regexp, URL, AnyOf, Optional, InputRequired, MacAddress, UUID, NoneOf
|
|
try:
|
|
from wtforms.validators import DataRequired
|
|
except ImportError:
|
|
# wtforms < 2.x
|
|
from wtforms.validators import Required as DataRequired
|
|
|
|
from wtforms.widgets import TextInput as _TextInput, PasswordInput as _PasswordInput, \
|
|
CheckboxInput as _CheckboxInput, Select as _Select, TextArea as _TextArea, \
|
|
ListWidget as _ListWidget, HiddenInput as _HiddenInput, RadioInput as _RadioInput, \
|
|
FileInput as _FileInput, Input
|
|
|
|
from wtforms.fields import StringField as _StringField, BooleanField as _BooleanField, \
|
|
DecimalField as _DecimalField, IntegerField as _IntegerField, \
|
|
FloatField as _FloatField, PasswordField as _PasswordField, \
|
|
SelectField as _SelectField, TextAreaField as _TextAreaField, \
|
|
RadioField as _RadioField, DateField as _DateField, \
|
|
DateTimeField as _DateTimeField, FileField as _FileField, \
|
|
SelectMultipleField as _SelectMultipleField
|
|
|
|
from wtforms.fields.html5 import URLField as _URLField, EmailField as _EmailField, \
|
|
DecimalRangeField as _DecimalRangeField, SearchField as _SearchField, \
|
|
TelField as _TelField, IntegerRangeField as _IntegerRangeField
|
|
|
|
from wtforms.widgets.html5 import ColorInput as _ColorInput, DateInput as _DateInput, \
|
|
DateTimeInput as _DateTimeInput, EmailInput as _EmailInput, \
|
|
RangeInput as _RangeInput, TelInput as _TelInput, URLInput as _UrlInput, \
|
|
NumberInput as _NumberInput, SearchInput as _SearchInput, TimeInput as _TimeInput, \
|
|
WeekInput as _WeekInput
|
|
|
|
|
|
def parsley_kwargs(field, kwargs):
|
|
"""
|
|
Return new *kwargs* for *widget*.
|
|
|
|
Generate *kwargs* from the validators present for the widget.
|
|
|
|
Note that the regex validation relies on the regex pattern being compatible with
|
|
both ECMA script and Python. The regex is not converted in any way.
|
|
It's possible to simply supply your own "parsley-regexp" keyword to the field
|
|
to explicitly provide the ECMA script regex.
|
|
See http://flask.pocoo.org/docs/patterns/wtforms/#forms-in-templates
|
|
|
|
Note that the WTForms url vaidator probably is a bit more liberal than the parsley
|
|
one. Do check if the behaviour suits your needs.
|
|
"""
|
|
new_kwargs = copy.deepcopy(kwargs)
|
|
|
|
if isinstance(field, DateField) or isinstance(field, DateTimeField):
|
|
_date_kwargs(new_kwargs, field)
|
|
if isinstance(field, IntegerField):
|
|
_integer_kwargs(new_kwargs)
|
|
if isinstance(field, DecimalField) or isinstance(field, FloatField):
|
|
_number_kwargs(new_kwargs)
|
|
if not 'data_trigger' in new_kwargs:
|
|
_trigger_kwargs(new_kwargs)
|
|
|
|
for vali in field.validators:
|
|
if isinstance(vali, Email):
|
|
_email_kwargs(new_kwargs)
|
|
if isinstance(vali, EqualTo):
|
|
_equal_to_kwargs(new_kwargs, vali)
|
|
if isinstance(vali, IPAddress):
|
|
_ip_address_kwargs(new_kwargs)
|
|
if isinstance(vali, Length):
|
|
_length_kwargs(new_kwargs, vali)
|
|
if isinstance(vali, NumberRange):
|
|
_number_range_kwargs(new_kwargs, vali)
|
|
if isinstance(vali, DataRequired) or isinstance(vali, InputRequired):
|
|
_input_required_kwargs(new_kwargs)
|
|
_trigger_kwargs(new_kwargs, u'key')
|
|
if isinstance(vali, Regexp) and not 'data_regexp' in new_kwargs:
|
|
_regexp_kwargs(new_kwargs, vali)
|
|
if isinstance(vali, URL):
|
|
_url_kwargs(new_kwargs)
|
|
if isinstance(vali, AnyOf):
|
|
_anyof_kwargs(new_kwargs, vali)
|
|
if isinstance(vali, MacAddress):
|
|
_mac_address_kwargs(new_kwargs)
|
|
if isinstance(vali, UUID):
|
|
_uuid_kwargs(new_kwargs)
|
|
if isinstance(vali, NoneOf):
|
|
_none_of_kwargs(new_kwargs, vali)
|
|
if isinstance(vali, Optional):
|
|
pass
|
|
|
|
if not 'parsley-error-message' in new_kwargs \
|
|
and not isinstance(vali, Optional) \
|
|
and vali.message is not None:
|
|
_message_kwargs(new_kwargs, message=vali.message)
|
|
|
|
return new_kwargs
|
|
|
|
|
|
def _email_kwargs(kwargs):
|
|
kwargs[u'data-parsley-type'] = u'email'
|
|
|
|
|
|
def _equal_to_kwargs(kwargs, vali):
|
|
kwargs[u'data-parsley-equalto'] = u'#' + vali.fieldname
|
|
|
|
|
|
def _ip_address_kwargs(kwargs):
|
|
# Regexp from http://stackoverflow.com/a/4460645
|
|
kwargs[u'data-parsley-pattern'] =\
|
|
r'^\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.' \
|
|
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.' \
|
|
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.' \
|
|
r'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b$'
|
|
|
|
|
|
def _length_kwargs(kwargs, vali):
|
|
default_number = -1
|
|
|
|
if vali.max != default_number and vali.min != default_number:
|
|
kwargs[u'data-parsley-length'] = u'[' + str(vali.min) + u',' + str(vali.max) + u']'
|
|
else:
|
|
if vali.max == default_number:
|
|
kwargs[u'data-parsley-minlength'] = str(vali.min)
|
|
if vali.min == default_number:
|
|
kwargs[u'data-parsley-maxlength'] = str(vali.max)
|
|
|
|
|
|
def _number_range_kwargs(kwargs, vali):
|
|
kwargs[u'data-parsley-range'] = u'[' + str(vali.min) + u',' + str(vali.max) + u']'
|
|
|
|
|
|
def _input_required_kwargs(kwargs):
|
|
kwargs[u'data-parsley-required'] = u'true'
|
|
|
|
|
|
def _regexp_kwargs(kwargs, vali):
|
|
# Apparently, this is the best way to check for RegexObject Type
|
|
# It's needed because WTForms allows compiled regexps to be passed to the validator
|
|
RegexObject = type(re.compile(''))
|
|
if isinstance(vali.regex, RegexObject):
|
|
regex_string = vali.regex.pattern
|
|
else:
|
|
regex_string = vali.regex
|
|
kwargs[u'data-parsley-pattern'] = regex_string
|
|
|
|
|
|
def _url_kwargs(kwargs):
|
|
kwargs[u'data-parsley-type'] = u'url'
|
|
|
|
|
|
def _anyof_kwargs(kwargs, vali):
|
|
# The inlist validator is no longer available in Parsley 2.x, so a custom anyof validator is used.
|
|
kwargs[u'data-parsley-anyof'] = json.dumps(vali.values)
|
|
|
|
|
|
def _mac_address_kwargs(kwargs):
|
|
kwargs[u'data-parsley-pattern'] = '^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$'
|
|
|
|
|
|
def _uuid_kwargs(kwargs):
|
|
kwargs[u'data-parsley-pattern'] = '^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$'
|
|
|
|
|
|
def _none_of_kwargs(kwargs, vali):
|
|
# data-parsley-noneof is a custom validator, it can be found in scripts/parsley-noneof.js
|
|
kwargs[u'data-parsley-noneof'] = json.dumps(vali.values)
|
|
|
|
|
|
def _trigger_kwargs(kwargs, trigger=u'change'):
|
|
kwargs[u'data-parsley-trigger'] = trigger
|
|
|
|
|
|
def _message_kwargs(kwargs, message):
|
|
kwargs[u'data-parsley-error-message'] = message
|
|
|
|
|
|
def _date_kwargs(kwargs, field):
|
|
kwargs[u'data-parsley-datefield'] = field.format
|
|
|
|
|
|
def _integer_kwargs(kwargs):
|
|
kwargs[u'data-parsley-type'] = "integer"
|
|
|
|
|
|
def _number_kwargs(kwargs):
|
|
kwargs[u'data-parsley-type'] = "number"
|
|
|
|
|
|
class ParsleyInputMixin(Input):
|
|
def __call__(self, field, **kwargs):
|
|
kwargs = parsley_kwargs(field, kwargs)
|
|
return super(ParsleyInputMixin, self).__call__(field, **kwargs)
|
|
|
|
|
|
class TextInput(_TextInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class PasswordInput(_PasswordInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class HiddenInput(_HiddenInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class TextArea(_TextArea):
|
|
def __call__(self, field, **kwargs):
|
|
kwargs = parsley_kwargs(field, kwargs)
|
|
return super(TextArea, self).__call__(field, **kwargs)
|
|
|
|
|
|
class CheckboxInput(_CheckboxInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class ColorInput(_ColorInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class DateInput(_DateInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class DateTimeInput(_DateTimeInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class EmailInput(_EmailInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class RangeInput(_RangeInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class TelInput(_TelInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class URLInput(_UrlInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class NumberInput(_NumberInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class TimeInput(_TimeInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class WeekInput(_WeekInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class SearchInput(_SearchInput, ParsleyInputMixin):
|
|
pass
|
|
|
|
|
|
class RadioInput(_RadioInput):
|
|
def __init__(self, parsley_options):
|
|
self.parsley_options = parsley_options
|
|
|
|
def __call__(self, field, **kwargs):
|
|
kwargs.update(self.parsley_options)
|
|
return super(RadioInput, self).__call__(field, **kwargs)
|
|
|
|
|
|
class Select(_Select):
|
|
def __call__(self, field, **kwargs):
|
|
kwargs = parsley_kwargs(field, kwargs)
|
|
return super(Select, self).__call__(field, **kwargs)
|
|
|
|
|
|
class ListWidget(_ListWidget):
|
|
def __call__(self, field, **kwargs):
|
|
kwargs = parsley_kwargs(field, kwargs)
|
|
return super(ListWidget, self).__call__(field, **kwargs)
|
|
|
|
|
|
class FileInput(_FileInput):
|
|
def __call__(self, field, **kwargs):
|
|
kwargs = parsley_kwargs(field, kwargs)
|
|
return super(FileInput, self).__call__(field, **kwargs)
|
|
|
|
|
|
class StringField(_StringField):
|
|
def __init__(self, *args, **kwargs):
|
|
super(StringField, self).__init__(widget=TextInput(), *args, **kwargs)
|
|
|
|
|
|
class IntegerField(_IntegerField):
|
|
widget = TextInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(IntegerField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class RadioField(_RadioField):
|
|
widget = _ListWidget()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(RadioField, self).__init__(*args, **kwargs)
|
|
widget_kwargs = parsley_kwargs(self, {})
|
|
self.option_widget = RadioInput(widget_kwargs)
|
|
|
|
|
|
class BooleanField(_BooleanField):
|
|
widget = CheckboxInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(BooleanField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class DecimalField(_DecimalField):
|
|
widget = TextInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(DecimalField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class FloatField(_FloatField):
|
|
widget = TextInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(FloatField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class PasswordField(_PasswordField):
|
|
widget = PasswordInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(PasswordField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class HiddenField(_PasswordField):
|
|
widget = HiddenInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(HiddenField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class TextAreaField(_TextAreaField):
|
|
widget = TextArea()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(TextAreaField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class SelectField(_SelectField):
|
|
widget = Select()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(SelectField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class DateTimeField(_DateTimeField):
|
|
widget = TextInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(DateTimeField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class DateField(_DateField):
|
|
widget = TextInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(DateField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class FileField(_FileField):
|
|
widget = FileInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(FileField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class SelectMultipleField(_SelectMultipleField):
|
|
widget = Select(multiple=True)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(SelectMultipleField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class URLField(_URLField):
|
|
widget = URLInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(URLField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class TelField(_TelField):
|
|
widget = TelInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(TelField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class SearchField(_SearchField):
|
|
widget = SearchInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(SearchField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class EmailField(_EmailField):
|
|
widget = EmailInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(EmailField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class DecimalRangeField(_DecimalRangeField):
|
|
widget = RangeInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(DecimalRangeField, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class IntegerRangeField(_IntegerRangeField):
|
|
widget = NumberInput()
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(IntegerRangeField, self).__init__(*args, **kwargs)
|