Skip to content

Instantly share code, notes, and snippets.

@aanastasiou
Created May 11, 2012 12:21
Show Gist options
  • Save aanastasiou/2659299 to your computer and use it in GitHub Desktop.
Save aanastasiou/2659299 to your computer and use it in GitHub Desktop.
Using Pylon's Deform with Flask
"""A very simple example to show what is required to get the excellent deform module from the Pylon's project to work with Flask.
DeForm is a Pylon's module capable of automatically creating HTML forms that conform to a schema of classes defined in Python through the Colander module. The transformation between Schema<-->Form data is handled by another module called Peppercorn. More information about how these three modules work together can be found at: http://docs.pylonsproject.org/projects/deform/en/latest/?awesome
Although DeForm is a Pylon's project, it can also operate as a stand-alone module. This Gist contains all the necessary changes to objects provided by Flask so that DeForm can serialize and deserialize data posted through a Flask.Request.
The basic thing that this gist is trying to demonstrate is how to derive from a Flask.Request object in order to change the data type of the Flask.Request.form object that stores the data posted by the client to the server. By default, Flask uses a MultiDict into which the order by which the variables are posted by the client is not preserved. Unfortunately, this messes up the so called "markers" that are required by Peppercorn so that certain fields (such as sequences) are delineated (http://docs.pylonsproject.org/projects/peppercorn/en/latest/?awesome).
In this gist, i am simply changing the MultiDict of the Request to an OrderedMultiDict which seems to work. However, please note that according to werkzeug's documentation, OrderedMultiDict is at least one order of magnitude slower than MultiDict (http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.OrderedMultiDict).
The accompanying html file must be put in a /templates and any additional files in a /static directory as per Flask's default configuration. The index.html file is basic and bare just to demonstrate the key features that need to be present for Deform to handle widget data properly.
"""
import flask
from werkzeug.datastructures import OrderedMultiDict
import colander
from deform import Form
import peppercorn
#Deriving from flask.Request
class theRequest(flask.Request):
parameter_storage_class = OrderedMultiDict #This is the only parameter that is going to be different
#Building a very simple "dummy" schema using Colander
class simpleRecord(colander.MappingSchema):
description = colander.SchemaNode(colander.String(), validator = colander.Length(min = 1,max=50))
amount = colander.SchemaNode(colander.Int())
class dataList(colander.SequenceSchema):
item = simpleRecord()
#This may seem redundant but is actually required by Deform
class topList(colander.MappingSchema):
items = dataList()
#Creating the flask "application"
app = flask.Flask(__name__)
#Configuring the application
app.request_class = theRequest #This is what actually effects the change of the form object's type
#A standard entry point to be served accepting both GET and POST calls.
@app.route("/", methods=["POST","GET"])
def index():
if flask.request.method == "POST":
#Please note the "multi=True"
controlData = peppercorn.parse(flask.request.form.items(multi=True))
#Now just sort the data just to do some kind of processing
controlData['items'] = sorted(controlData['items'],key=lambda x:int(x['amount']))
#Now, create the form to be returned to the user according to the schema defined before and populating it with the data that were posted by the user and processed by the application so far.
someForm = Form(topList(),buttons=('submit',)).render(controlData)
aMess="Processed Data" #Just a string to define a message
else:
#Just return an empty form
someForm = Form(testSchema.topList(),buttons=('submit',)).render()
aMess = "You can use this form to sort a list of simple records"
return flask.render_template("index.html",theForm = someForm, theMessage = aMess)
if __name__=="__main__":
app.debug = True;
app.run()
<html>
<head>
<title>Ultimate Sorter</title>
<style>
.wrapper{
margin-left:auto;
margin-right:auto;
width:1024px;
height:128;
border:think solid grey;
font-family:Verdana;
}
.header{
width:inherit;
border-bottom:thin black solid;
}
.main{
width:inherit;
border-bottom:thin black solid;
}
.footer{
font-size:smaller
}
.errorBar{
text-align:center;
font-weight:bold;
font-style:italic;
color:white;
background-color:red;
height:2em;
}
</style>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!-- CSS if required-->
<link rel="stylesheet" href="/static/form.css" type="text/css" />
<!-- JavaScript deform.js is absolutely essential for the controls to operate properly-->
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="/static/deform.js"></script>
</head>
<!--deform.load() MUST be called -->
<body onload="deform.load()">
<div class="wrapper">
<div class="header">
<h1>Welcome...</h1>
<p>Some website</p>
</div>
<div class="message">
{{theMessage}}
</div>
<!--Please note the |safe designation in the following line so that "theForm's" content is not escaped -->
<div class="main">
{{ theForm |safe}}
</div>
<div class="footer">
<p>Blah blah</p>
</div>
</div>
</body>
</html>
@realgimpel
Copy link

Hi, the code above gives an error: testSchema (line 56) is not declared.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment