initial commit

This commit is contained in:
Johannes Gehrs
2013-06-23 23:52:59 +02:00
commit cb015025cf
8 changed files with 380 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
.DS_Store
*.pyc
*.pyo
env
env*
dist
*.egg
*.egg-info
_mailinglist
.tox
.idea
+3
View File
@@ -0,0 +1,3 @@
[submodule "sample/static/scripts/parsleyjs"]
path = sample/static/scripts/parsleyjs
url = https://github.com/guillaumepotier/Parsley.js.git
+62
View File
@@ -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)
+30
View File
@@ -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 %}
+66
View File
@@ -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 %}
+184
View File
@@ -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)