mirror of
https://github.com/wassname/wtforms-parsleyjs.git
synced 2026-07-27 11:29:25 +08:00
initial commit
This commit is contained in:
+11
@@ -0,0 +1,11 @@
|
||||
.DS_Store
|
||||
*.pyc
|
||||
*.pyo
|
||||
env
|
||||
env*
|
||||
dist
|
||||
*.egg
|
||||
*.egg-info
|
||||
_mailinglist
|
||||
.tox
|
||||
.idea
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "sample/static/scripts/parsleyjs"]
|
||||
path = sample/static/scripts/parsleyjs
|
||||
url = https://github.com/guillaumepotier/Parsley.js.git
|
||||
@@ -0,0 +1,62 @@
|
||||
from flask import Flask, render_template, redirect, url_for, request, flash
|
||||
from wtforms import Form, validators
|
||||
from wtforms_parsleyjs import IntegerField, BooleanField, SelectField, TextField
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route('/parsley_testform', methods=['GET', 'POST'])
|
||||
def parsley_testform():
|
||||
form = ParsleyTestForm(request.form)
|
||||
if request.method == 'POST': form.validate()
|
||||
return render_template('wtforms_parsley_sample.html', form=form)
|
||||
|
||||
|
||||
class ParsleyTestForm(Form):
|
||||
email = TextField('E-Mail Address', [
|
||||
validators.Email('Sorrry, not a valid email address.')
|
||||
], default='test@example.com')
|
||||
first_value = TextField('Some Value', default='Some value')
|
||||
second_value = TextField('Should be identical', [
|
||||
validators.EqualTo(message='Sorry, values do not match.',
|
||||
fieldname='first_value')
|
||||
], default='Some value')
|
||||
ip_address = TextField('IP4 Address', [
|
||||
validators.IPAddress(message='Sorry, not a valid IP4 Address.')
|
||||
], default='127.0.0.1')
|
||||
string_length = TextField('Length of String (5 to 10)', [
|
||||
validators.Length(message='Length should be between 5 and 10 characters.',
|
||||
min=5, max=10)
|
||||
], default='Hello!')
|
||||
number_range = IntegerField('Number Range (5 to 10)', [
|
||||
validators.NumberRange(message='Range should be between 5 and 10.',
|
||||
min=5, max=10)
|
||||
], default=7)
|
||||
|
||||
required_text = TextField('Required Field', [
|
||||
validators.Required(message='Sorry, this is a required field.')
|
||||
], default='Mandatory text')
|
||||
required_select = SelectField('Required Select', [
|
||||
validators.Required(
|
||||
message='Sorry, you have to make a choice.')
|
||||
], choices=[('', 'Please select an option'), ('cpp', 'C++'), ('py', 'Python'),
|
||||
('text', 'Plain Text')])
|
||||
required_checkbox = BooleanField('Required Checkbox', [
|
||||
validators.Required(message='Sorry, you need to accept this.')
|
||||
])
|
||||
|
||||
regexp = TextField('Regex-Matched Hex Color-Code', [
|
||||
validators.Regexp(message='Not a proper color code, sorry.',
|
||||
regex=r'^#[A-Fa-f0-9]{6}$')
|
||||
], default='#7D384F')
|
||||
url = TextField('URL Field', [
|
||||
validators.URL(message='Sorry, this is not a valid URL,')
|
||||
], default='http://example.com/parsley')
|
||||
anyof = TextField('Car, Bike or Plane?', [
|
||||
validators.AnyOf(message='Sorry, you can only choose from car, bike and plane',
|
||||
values=['car', 'bike', 'plane'])
|
||||
], default='car')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
Submodule
+1
Submodule sample/static/scripts/parsleyjs added at 4dfaaabfd0
@@ -0,0 +1,30 @@
|
||||
{% macro render_checkbox(field) %}
|
||||
<div class="control-group {% if field.errors %} error {% endif %}">
|
||||
<div class="controls">
|
||||
<label class="checkbox">{{ field(**kwargs)|safe }} {{ field.label }}</label>
|
||||
{{ render_errors(field) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_field(field) %}
|
||||
<label class="control-label" for="{{ field.name }}">{{ field.label }}</label>
|
||||
<div class="control-group {% if field.errors %} error {% endif %}">
|
||||
<div class="controls">
|
||||
{{ field(**kwargs)|safe }}
|
||||
{{ render_errors(field) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_errors(field) %}
|
||||
{% if field.errors %}
|
||||
<span class="help-inline">
|
||||
<ul>
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
@@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>WTForms-Parsley Sample</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
{% block styles %}
|
||||
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css"
|
||||
rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
|
||||
}
|
||||
</style>
|
||||
{% endblock styles %}
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<button type="button" class="btn btn-navbar" data-toggle="collapse"
|
||||
data-target=".nav-collapse">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="brand" href={{ url_for('parsley_testform') }}>WTForms-Parsley
|
||||
Sample</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
{% block content %}
|
||||
|
||||
{% endblock content %}
|
||||
</div>
|
||||
<!-- /container -->
|
||||
|
||||
{% block scripts %}
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
|
||||
<script src="{{ url_for('static', filename='scripts/parsley/parsley.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='scripts/parsley/parsley.extend.js') }}"></script>
|
||||
<script>$('form').parsley({ successClass: 'success', errorClass: 'error',
|
||||
errors: {
|
||||
classHandler: function (element) {
|
||||
return $(element).parent().parent();
|
||||
},
|
||||
container: function (element) {
|
||||
var $container = element.parent().find(".help-inline");
|
||||
if ($container.length === 0) {
|
||||
$container = $("<span class='help-inline'></span>").insertAfter(element);
|
||||
}
|
||||
return $container;
|
||||
}
|
||||
}});
|
||||
</script>
|
||||
{% endblock scripts %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,23 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<p class="lead">Check out the form validation!</p>
|
||||
{% from "_formhelpers.html" import render_field, render_checkbox %}
|
||||
<form method=post action="{{ url_for('parsley_testform') }}">
|
||||
<fieldset>
|
||||
{{ render_field(form.email) }}
|
||||
{{ render_field(form.first_value) }}
|
||||
{{ render_field(form.second_value) }}
|
||||
{{ render_field(form.ip_address) }}
|
||||
{{ render_field(form.string_length) }}
|
||||
{{ render_field(form.number_range) }}
|
||||
{{ render_field(form.required_text) }}
|
||||
{{ render_field(form.required_select) }}
|
||||
{{ render_checkbox(form.required_checkbox) }}
|
||||
{{ render_field(form.regexp) }}
|
||||
{{ render_field(form.url) }}
|
||||
{{ render_field(form.anyof) }}
|
||||
<p><input type=submit value=Submit>
|
||||
</fieldset>
|
||||
</form>
|
||||
{% endblock content %}
|
||||
@@ -0,0 +1,184 @@
|
||||
__author__ = 'Johannes Gehrs (jgehrs@gmail.com)'
|
||||
|
||||
import re, copy
|
||||
from wtforms.validators import Length, NumberRange, Email, EqualTo, IPAddress, \
|
||||
Required, Regexp, URL, AnyOf
|
||||
from wtforms import TextField
|
||||
from wtforms.widgets import TextInput as _TextInput, PasswordInput as _PasswordInput, \
|
||||
CheckboxInput as _CheckboxInput, Select as _Select, Input
|
||||
from wtforms.fields import TextField as _TextField, BooleanField as _BooleanField, \
|
||||
DecimalField as _DecimalField, IntegerField as _IntegerField, \
|
||||
FloatField as _FloatField, PasswordField as _PasswordField, \
|
||||
SelectField as _SelectField
|
||||
|
||||
def parsley_kwargs(field, kwargs):
|
||||
"""
|
||||
Return new *kwargs* for *widget*.
|
||||
|
||||
Generate *kwargs* from the validators present for the field.
|
||||
|
||||
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 "data-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, Required):
|
||||
_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 not 'data_trigger' in new_kwargs:
|
||||
_trigger_kwargs(new_kwargs)
|
||||
if not 'data-error-message' in new_kwargs and vali.message is not None:
|
||||
_message_kwargs(new_kwargs, message=vali.message)
|
||||
|
||||
return new_kwargs
|
||||
|
||||
|
||||
def _email_kwargs(kwargs):
|
||||
kwargs[u'data-type']=u'email'
|
||||
|
||||
|
||||
def _equal_to_kwargs(kwargs, vali):
|
||||
kwargs[u'data-equalto']=u'#' + vali.fieldname
|
||||
|
||||
|
||||
def _ip_address_kwargs(kwargs):
|
||||
# Regexp from http://stackoverflow.com/a/4460645
|
||||
kwargs[u'data-regexp']=\
|
||||
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):
|
||||
kwargs[u'data-rangelength'] = u'[' + str(vali.min) + u',' + str(vali.max) + u']'
|
||||
|
||||
|
||||
def _number_range_kwargs(kwargs, vali):
|
||||
kwargs[u'data-range'] = u'[' + str(vali.min) + u',' + str(vali.max) + u']'
|
||||
|
||||
|
||||
def _required_kwargs(kwargs):
|
||||
kwargs[u'data-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-regexp'] = regex_string
|
||||
|
||||
|
||||
def _url_kwargs(kwargs):
|
||||
kwargs[u'data-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.
|
||||
delimiter = u','
|
||||
for value in vali.values:
|
||||
if value.find(',') != -1:
|
||||
delimiter = u';;;'
|
||||
break
|
||||
if delimiter != u',': kwargs[u'data-inlist-delimiter'] = delimiter
|
||||
return delimiter
|
||||
|
||||
|
||||
def _anyof_kwargs(kwargs, vali):
|
||||
delimiter = _string_seq_delimiter(vali, kwargs)
|
||||
kwargs[u'data-inlist'] = delimiter.join(vali.values)
|
||||
|
||||
|
||||
def _trigger_kwargs(kwargs, trigger=u'change'):
|
||||
kwargs[u'data-trigger']=trigger
|
||||
|
||||
|
||||
def _message_kwargs(kwargs, message):
|
||||
kwargs[u'data-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 CheckboxInput(_CheckboxInput, ParsleyInputMixin):
|
||||
pass
|
||||
|
||||
|
||||
class Select(_Select):
|
||||
def __call__(self, field, **kwargs):
|
||||
kwargs = parsley_kwargs(field, kwargs)
|
||||
return super(Select, self).__call__(field, **kwargs)
|
||||
|
||||
|
||||
class TextField(_TextField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TextField, self).__init__(widget = TextInput(), *args, **kwargs)
|
||||
|
||||
|
||||
class IntegerField(_IntegerField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(IntegerField, self).__init__(widget = TextInput(), *args, **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 SelectField(_SelectField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SelectField, self).__init__(widget = Select(), *args, **kwargs)
|
||||
Reference in New Issue
Block a user