Latest Version: 0.9.6.2
  Dashboard > Pylons Cookbook > ... > Getting Started With ToscaWidgets and Pylons > ToscaWidgets and Mako - example app
  Pylons Cookbook Log In | Sign Up   View a printable version of the current page.  
  ToscaWidgets and Mako - example app
Added by Martin Vool, last edited by Martin Vool on Jun 28, 2007  (view change)
Labels: 
(None)

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,

1
2
3
4
@validate(form=form, error_handler='index')
def save(self, id="None"):
    c.showmytext = "This is great, you are not a retard!!!"
    return render_response('/base.mako')

Note that we no longer need to call valid() or even define it in MyProject/myproject/forms/widgets/__init__.py

Posted by Brendan Arnold at Jul 15, 2007 14:57 | Permalink

I am trying to use the Calendar date picker using this script, & can't work out how to enable the required javascript files to be loaded into the page header (or add any CSS params for that matter). It seems that the directions for adding params using this approach has some issues...

Has anyone managed to solve this problem yet?

Posted by Bruce Coble at Jul 17, 2007 00:55 | Permalink
Site running on a free Atlassian Confluence Open Source Project License granted to Pylons. Evaluate Confluence today.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.3.3 Build:#645 Feb 13, 2007) - Bug/feature request - Contact Administrators
Top