What is this?
I have struggled about a week to get toscawidgets, pylons, mako work togeather. I am totally new to python. Now i have built something and i think that i am beginning to understand. This is based on the other articles about TW and mako but tryes to show a combined example of an app and make things understandable to people with no or almost no experience in python.
I am planing to show here how-to save the posted data with SQLAlchemy(or elixir, we will see) and then get the info out of the database.
But i dont know it myself so be patient.
Maybe this article should be merged with An Alternative ToscaWidgets Setup with Mako or Custom ToscaWidgets Forms
This article is far from finished
Please let me know of any spelling or programming mistakes or typos 
Set-up Pylons, toscawidgets and mako.
This is already covered by the articles in the coockbook, so i wond repeat it here.
An Alternative ToscaWidgets Setup with Mako - Follow the instructions for the middleware
Follow the next steps if you can get pylons up and running and maybe you should go through all of An Alternative ToscaWidgets Setup with Mako
File Layout
This is based on Custom ToscaWidgets Forms, but a bit different.
Files to create:
1
2
3
4
5
6
7 | MyProject/myproject/forms #create a folder
MyProject/myproject/forms/__init__.py #just a plain empty text file(or import wont work)
MyProject/myproject/forms/widgets/__init__.py #this is where we define reusable forms and stuff
MyProject/myproject/templates/custom_field.mako #the template for handling FOO<fomr>FOO [inputs] FOO</form>FOO
MyProject/myproject/templates/custom_form.mako #To create a custom INPUT
MyProject/myproject/templates/base.mako #Just to display the whole thing.
|
It is fairly easy to mix up form and field 
Templates
This is stolen from Custom ToscaWidgets Forms
Fist we create a FORM template. This is how your INPUTS will be arranged.
Location: MyProject/myproject/templates/custom_form.mako
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 | <form
id="${context.get('id')}"
name="${name}"
action="${action}"
method="${method}"
class="${css_class}"
% for k, v in attrs.items():
% if v is not None:
${k}="${v}"
% endif
% endfor
>
<div>
% for field in ihidden_fields:
<div>
<%
error=error_for(field)
%>
${field.display(value_for(field), **args_for(field))}
% if show_children_errors and error and not field.show_error:
<span class="fielderror">${error}</span>
% endif
</div>
% endfor
</div>
<table border="0" cellspacing="0" cellpadding="2"
% for k, v in custom_attrs.items():
% if v is not None:
${k}="${v}"
% endif
% endfor
>
% for i, field in enumerate(ifields):
<%
required = [None,' required'][int(field.is_required)]
error = error_for(field)
label_text = field.label_text
help_text = field.help_text
%>
<tr class="${i%2 and 'odd' or 'even'}">
<th>
% if label_text:
<label
id="${field.id}.label"
for="${field.id}"
class="fieldlabel${required}"
>${label_text}</label>
% endif
</th>
<td>
${field.display(value_for(field), **args_for(field))}
% if help_text:
<span class="fieldhelp">${help_text}</span>
% endif
% if show_children_errors and error and not field.show_error:
<span class="fielderror">${error}</span>
% endif
</td>
</tr>
% endfor
</table>
</form>
|
While we are at creating templates, lets create a custom FIELD template. Do not start to edit the templates now, just try to get things up and running the way they are at the moment.
Location: MyProject/myproject/templates/custom_field.mako
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | <input
% if context.get('type'):
type="${context.get('type')}"
% endif
name="${name}"
class="${css_class}"
id="${context.get('id')}"
value="${value}"
% for k, v in attrs.items():
% if v is not None:
${k}="${v}"
% endif
% endfor
/>
|
This is just a simple base layout for our sample.
Location: MyProject/myproject/templates/base.mako
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//ET"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html><head>
<title>My Homepage</title>
<body bgcolor='#FFFFFF' text='#000000' scroll='auto'>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
</head>
<body topmargin='0'>
<center>
Hello and welcome to my homepage, please fill in this simple form
<table>
<tr><td>
${c.showmytext}
</td></tr></table>
|
Widget
Location: MyProject/myproject/forms/widgets/__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113 | #I think i didnt import things that arent needed, but i am not sure.
from formencode.validators import *
from formencode.schema import Schema
from YOURPROJECT.lib.base import *
#dont forget to change
from toscawidgets.widgets.forms.fields import Form
from toscawidgets.api import WidgetsList, CSSSource, JSSource
from toscawidgets.js import js_function
from toscawidgets.widgets.forms import *
#i have no idea why the hell i need this, maybe it is for inserting values to fields.
#note to self: Find out what it is.
class CustomFormMixin:
"""
Mix-in class for containers that use a custom method to render their fields
"""
params = ["custom_attrs"]
custom_attrs = {}
#Here the *FORM* finds its template.
#Note: the other examples did use css and javascript here, but for some reason it didn't work for me :-(
#When i get the css and javascript things to work i will let you know
#You could put your css directly into the form template tho.
class CustomForm(Form, CustomFormMixin):
"""
A form that renders it's fields in a custom way
"""
template = "mako:custom_form.mako"
#This is the place where your Custom Input will load its template.
#I should remind you that you probably wont need a custom field because almost all types of fields are covered already, but let it be here.
#Go see: http://toscawidgets.org/module-toscawidgets.widgets.forms.fields.html
class CustomField(FormField):
template = "mako:custom_field.mako"
#This is the fun part.
#Here you define what kind of form layout to use and the you define all the fields that should be there.
#Later you can just create more and more of these, for different forms with different inputs. :-)
class MyForm(CustomForm):
class fields(WidgetsList):
#create a hidden field with the name=id and value="I'm hidden!"
id = HiddenField(
default="I'm hidden!"
)
#creates <input type=text name=name value=Your...>
name = TextField(
#This is so cool, you just say what kind of input you expect here and it just works :-) This has to be text and not empty
#All sorts of cases are covered: Email, URL, Date, Time and so on.
#Take a look at http://toscawidgets.org/module-toscawidgets.widgets.forms.validators.html
validator = UnicodeString(not_empty=True),
default = "Your name here"
)
#another field. Go see http://toscawidgets.org/module-toscawidgets.widgets.forms.html for more details.
age = SingleSelectField(
validator = Int,
options = range(100)
)
#This is our own custom field that we have created before.
custom = CustomField(
validator = Int,
default = '10',
label_text = 'Custom field',
)
#This is for form valiation...i dont know how it works but i think this is the best place for it.
#This valiates all the forms, so yhis is static and you dont have to edit it.
#Stolen from [An Alternative ToscaWidgets Setup with Mako]
def valid(self, form=None, validators=None, post_only=True, state_factory=None):
# These two lines added based on Steven's comments below
if not request.method == 'POST':
return False
# end the lines added based on Steven's comment
if post_only:
params = request.POST.copy()
else:
params = request.params.copy()
errors = {}
state = None
if state_factory:
state = state_factory()
if form:
try:
self.form_result = form.validate(params, state=state)
except Invalid, e:
self.validation_exception = e
errors = e.error_dict
if validators:
if isinstance(validators, dict):
if not hasattr(self, 'form_result'):
self.form_result = {}
for field, validator in validators.iteritems():
try:
self.form_result[field] = \
validator.to_python(decoded[field] or None, state)
except Invalid, error:
errors[field] = error
if errors:
self.errors = errors
return False
return True
|
Almost there!!!
Now you can open up your desired controller in the controllers folder.
First we need to import all the important stuff 
1
2
3
4
5
6
7
8
9
10
11 | from YOURPROJECT.lib.base import *
from YOURPROJECT.forms.widgets import *
#Dont forget to change
from toscawidgets.mods.pylonshf import validate, render_response
from toscawidgets.api import WidgetBunch
from formencode import Invalid
from toscawidgets.view import EngineManager
from toscawidgets.middleware import TGWidgetsMiddleware
from toscawidgets.mods.pylonshf import PylonsHostFramework
from pylons.templating import render
|
Now we can call the MyForm class if we want to display our forms.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | #NOTE: action="save" this directs the form to the def save :-)
form=MyForm(name='test', action="save", submit_text='Submit', method='post', class_='test', custom_attrs={"class":"form"})
#so we wouldn't have to call it many times...and it would be easely accessable
class IndexController(BaseController):
def index(self):
#this will show our created form.
#c.showmytext shows the text in base.mako
c.showmytext=form
return render_response('/base.mako')
def save(self, id="None"):
if not valid(self, form):
#Goes back to index, if forms are not valid
return self.index()
#but if they are good then goes on from here.
c.showmytext = "This is great, you are not a retard!!!"
return render_response('/base.mako')
|
How-to set other values.
Based on An Alternative ToscaWidgets Setup with Mako
Open up your controller. And add this after the "from foo include..." These values should come from the models, but this is an example.
1
2
3
4
5 | class Othervalues(object):
name = "Timmy"
age = "15"
custom = "20"
value = Othervalues()
|
Now we change the def index like this.
1
2
3
4
5 | def index(self):
#this will show our created form.
#only the next line is changed, you can pass other things too, eg c.showmytext=form.display(value, action=c.action)}
c.showmytext=form.display(value)
return render_response('/base.mako')
|
If you don't need to add custom validation you can use the @validate decorator as defined by ToscaWidgets (slightly different to the Pylons one).
So add the following decorator to the save action so that it reads,
Note that we no longer need to call valid() or even define it in MyProject/myproject/forms/widgets/__init__.py