Files
wtforms-parsleyjs/wtformsparsleyjs/core.py
T

260 lines
8.8 KiB
Python

__author__ = 'Johannes Gehrs (jgehrs@gmail.com)'
import re
import copy
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 import StringField
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, \
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
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 eegex 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)
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 'data_trigger' in new_kwargs:
_trigger_kwargs(new_kwargs)
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 _string_seq_delimiter(vali, kwargs):
# We normally use a comma as the delimiter - looks clean and it's parsley's default.
# If the strings for which we check contain a comma, we cannot use it as a delimiter.
default_delimiter = u','
fallback_delimiter = u';;;'
delimiter = default_delimiter
for value in vali.values:
if value.find(',') != -1:
delimiter = fallback_delimiter
break
if delimiter != default_delimiter:
kwargs[u'data-parsley-inlist-delimiter'] = delimiter
return delimiter
def _anyof_kwargs(kwargs, vali):
delimiter = _string_seq_delimiter(vali, kwargs)
kwargs[u'data-parsley-inlist'] = delimiter.join(vali.values)
def _mac_addresss_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):
delimiter = _string_seq_delimiter(vali, kwargs)
#data-parsley-noneof is a custom validator, it can be found in scripts/parsley-noneof.js
kwargs[u'data-parsley-noneof'] = delimiter.join(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
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, ParsleyInputMixin):
pass
class CheckboxInput(_CheckboxInput, 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 StringField(_StringField):
def __init__(self, *args, **kwargs):
super(StringField, self).__init__(widget=TextInput(), *args, **kwargs)
class IntegerField(_IntegerField):
def __init__(self, *args, **kwargs):
super(IntegerField, self).__init__(widget=TextInput(), *args, **kwargs)
class RadioField(_RadioField):
def __init__(self, *args, **kwargs):
super(RadioField, self).__init__(widget=_ListWidget(), *args, **kwargs)
widget_kwargs = parsley_kwargs(self, {})
self.option_widget = RadioInput(widget_kwargs)
class BooleanField(_BooleanField):
def __init__(self, *args, **kwargs):
super(BooleanField, self).__init__(widget=CheckboxInput(), *args, **kwargs)
class DecimalField(_DecimalField):
def __init__(self, *args, **kwargs):
super(DecimalField, self).__init__(widget=TextInput(), *args, **kwargs)
class FloatField(_FloatField):
def __init__(self, *args, **kwargs):
super(FloatField, self).__init__(widget=TextInput(), *args, **kwargs)
class PasswordField(_PasswordField):
def __init__(self, *args, **kwargs):
super(PasswordField, self).__init__(widget=PasswordInput(), *args, **kwargs)
class HiddenField(_PasswordField):
def __init__(self, *args, **kwargs):
super(HiddenField, self).__init__(widget=HiddenInput(), *args, **kwargs)
class TextAreaField(_TextAreaField):
def __init__(self, *args, **kwargs):
super(TextAreaField, self).__init__(widget=TextArea(), *args, **kwargs)
class SelectField(_SelectField):
def __init__(self, *args, **kwargs):
super(SelectField, self).__init__(widget=Select(), *args, **kwargs)