Created
February 3, 2014 11:40
-
-
Save molszewski/8782476 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!venv/bin/python | |
# Requirements: | |
# - virtualenv in venv dir (virtualenv venv) | |
# - Flask: venv/bin/pip install flask | |
# - formencode: venv/bin/pip install formencode | |
# - FormEncode-Jinja2: venv/bin/pip install FormEncode-Jinja2 | |
from flask import render_template_string, flash, redirect, request | |
from formencode import Invalid, variabledecode, Schema, All | |
from formencode.validators import Int, UnicodeString, FancyValidator, MinLength | |
from formencode.schema import SimpleFormValidator | |
from datetime import date | |
from flask import Flask | |
import formencode_jinja2 | |
############################################################## | |
# App configuration | |
app = Flask(__name__) | |
app.jinja_env.add_extension(formencode_jinja2.formfill) | |
app.config.update( | |
DEBUG=True, | |
SECRET_KEY='so key much secret' | |
) | |
############################################################## | |
# Validation schemas | |
MONTH_OPTIONS = ( | |
'January', | |
'February', | |
'March', | |
'April', | |
'May', | |
'June', | |
'July', | |
'August', | |
'September', | |
'October', | |
'November', | |
'December' | |
) | |
class MonthValidator(FancyValidator): | |
def _to_python(self, value, state): | |
return value.strip() | |
def validate_python(self, value, state): | |
if value not in MONTH_OPTIONS: | |
raise Invalid('Invalid month name. Valid are ' + ', '.join(MONTH_OPTIONS), value, state) | |
return value | |
def validate_date_of_birth(value_dict, state, validator): | |
day = value_dict['day'] | |
month = value_dict['month'] | |
year = value_dict['year'] | |
numeric_month = MONTH_OPTIONS.index(month) | |
try: | |
parsed_date = date(year, numeric_month + 1, day) | |
except: | |
raise Invalid('Date is invalid!', value_dict, state) | |
if parsed_date.year < 1900 or parsed_date >= date.today(): | |
raise Invalid('Date < 1900 or in future', value_dict, state) | |
return None | |
class DateOfBirth(Schema): | |
day = Int(not_empty=True, strip=True) | |
month = MonthValidator(not_empty=True, strip=True) | |
year = Int(not_empty=True, strip=True) | |
chained_validators = [ | |
SimpleFormValidator(validate_date_of_birth) | |
] | |
class Registration(Schema): | |
username = All(MinLength(10), UnicodeString(not_empty=True, strip=True)) | |
dob = DateOfBirth() | |
############################################################## | |
# Template | |
REGISTRATION_TEMPLATE = ''' | |
<html> | |
<head> | |
<title>formencode Spike</title> | |
</head> | |
<body> | |
{% with messages = get_flashed_messages() %} | |
{% if messages %} | |
<ul> | |
{% for message in messages %} | |
<li>{{ message }} </li> | |
{% endfor %} | |
</ul> | |
{% endif %} | |
{% endwith %} | |
<h1>Register</h1> | |
<div> | |
{{debug_info}} | |
</div> | |
<form action="" method="post" name="login"> | |
<p>Dude, please register:</p> | |
<p> | |
{%- formfill form_data with errors -%} | |
<span>Name</span><input type="text" name="username"/><form:error name="username" /><br/> | |
<span>DOB</span> | |
<form:iferror name="dob"><span style="color:red">Start of DOB error [[[</form:iferror> | |
<input type="number" name="dob.day"/><form:error name="dob.day" /> | |
<input type="text" name="dob.month"/><form:error name="dob.month" /> | |
<input type="number" name="dob.year"/><form:error name="dob.year" /> | |
<form:error name="dob" /> | |
<form:iferror name="dob">]]] End of DOB error</span></form:iferror> | |
{%- endformfill -%} | |
</p> | |
<p><input type="submit" value="Hit me, pal"></p> | |
</form> | |
</body> | |
</html> | |
''' | |
INDEX_TEMPLATE=''' | |
<html> | |
<head> | |
<title>formencode Spike</title> | |
</head> | |
<body> | |
{% with messages = get_flashed_messages() %} | |
{% if messages %} | |
<ul> | |
{% for message in messages %} | |
<li>{{ message }} </li> | |
{% endfor %} | |
</ul> | |
{% endif %} | |
{% endwith %} | |
<h1>Hello, please <a href="/register">register here</a></h1> | |
</body> | |
</html> | |
''' | |
############################################################## | |
# App routes | |
@app.route('/') | |
@app.route('/index') | |
def index(): | |
return render_template_string(INDEX_TEMPLATE) | |
@app.route('/register', methods=['GET', 'POST']) | |
def schema(): | |
schema = Registration() | |
form_data = request.form | |
if form_data: | |
try: | |
decoded = variabledecode.variable_decode(form_data) | |
result = schema.to_python(decoded) | |
flash('Submitted ' + str(variabledecode.variable_encode(result))) | |
return redirect('/index') | |
except Invalid, e: | |
unpacked_errors = e.unpack_errors(variabledecode.variable_encode) | |
return render_template_string(REGISTRATION_TEMPLATE, | |
title='Register', | |
form_data=form_data, | |
errors=unpacked_errors) | |
else: | |
return render_template_string(REGISTRATION_TEMPLATE, | |
title='Register', | |
form_data=form_data) | |
############################################################## | |
# App start | |
app.run(debug=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment