Warning: Can't synchronize with repository "(default)" (Unsupported version control system "svn": No module named svn). Look in the Trac log for more information.

Changes between Version 27 and Version 28 of SimpleWidgetForm

01/13/07 16:55:29 (13 years ago)

docs migration


  • SimpleWidgetForm

    v27 v28  
    1 '''Note:''' Check the bottom of the page for the example project. 
    3 = Simple Widget Form Tutorial = 
    5 This is a TurboGears (http://www.turbogears.org) project. It can be 
    6 started by running the start-formstutorial.py script. 
    8 This tutorial is designed to introduce TurboGears 0.8 users to the new 
    9 widgets, error handling, and decorators for Turbogears 0.9. It is current as 
    10 of version 0.9a4 and should be valid through 0.9 final. 
    12 This application accepts and displays comments. Unlike most TurboGears 
    13 projects, this application uses in-memory storage in a global ``comments`` 
    14 object rather than in a database. The comments are listed on the index page 
    15 and a link at the bottom of the page directs the user to add a comment. 
    17 The comments form itself should require little explanation, but it requires 
    18 both a name and an email address to be entered. If the name or email fields 
    19 are missing, the form is redisplayed along with a message next to the 
    20 appropriate field. 
    22 Successful form entry adds the comment to the global comments variable and 
    23 displays a success message on the index page. 
    25 == Starting the Project == 
    27 As in 0.8, tg-admin quickstart creates a new project. This project was created 
    28 with the name `FormsTutorial`. 
    30 * Note: This tutorial does not contain step-by-step instructions to create the final project. 
    31   Instead, download and unpack the final project and read the source code 
    32   along with the tutorial. 
    34 The first thing users upgrading from 0.8 should notice is that the configuration 
    35 layout has changed a bit. 
    36 All configuration that was previously duplicated between dev and prod 
    37 has been moved to `formstutorial/config/app.cfg`. 
    39 Some features of the `app.cfg` file to note (but which will 
    40 not be discussed): 
    42 * You can now change the templating engine basis by installing a new 
    43   templating plugin. Control which is active by setting the 
    44   `tg.defaultview` setting. 
    45 * TurboGears now requires you to explicitly state that an exposed method can 
    46   be formatted as json. You can restore the 0.8 behavior by setting the 
    47   `tg.allow_json` variable to true. 
    48 * TurboGears now comes with Visit Tracking and an Identity framework. Both 
    49   are disabled by default. 
    51 == Starting from the index() == 
    53 Open up `formstutorial/controllers.py` in your favorite text editor. Ignore 
    54 the first part and skip down to the `Root` class. The first major change 
    55 here is the new decorators. 
    57 In earlier versions of 0.9 svn, the `@expose` decorator both published the 
    58 decorated method and provided form validation. This was deemed to be 
    59 inflexible, and in 0.9 `@expose` provides publishing related functions 
    60 (template, engine, allow_json, etc) while the new `@validate` decorator 
    61 dictates which form (keep reading, forms are coming) to use for input 
    62 processing. 
    64 You may notice that templates passed to `@expose` in this example don't 
    65 start with "formstutorial". This is the new relative import feature in Kid. 
    66 Naming your templates this way simplifies project renaming. 
    68 Take a quick glance at `formstutorial/templates/welcome.kid`.  
    70 The main feature 
    71 to notice here is the use of `std.url` for the link. If your project was 
    72 mounted at `http://blah/foo/`, `std.url` will take the '/foo/' into 
    73 account and send your users to '/foo/add' rather than the incorrect '/add'. 
    75 We've exhausted the interesting bits of index, now lets move on to `add`. 
    77 == Intro to Forms and Widgets == 
    79 The ``formstutorial/templates/form.kid`` is a completely generic form 
    80 displaying template. It takes an optional title (default is 'Forms Tutorial') 
    81 along with a form and an action. Setting your form template up this way makes 
    82 for easy prototyping and I prefer it to creating a new template for each form. 
    84 All the action on this page is being handled by the new widgets system. 
    85 Widgets provide a convenient way to bundle appearance and behavior and make 
    86 form creation really easy. 
    88 Widgets are split up into three general categories: Forms, Fields, and 
    89 Fastdata. Forms act as containers for Fields providing a layout (Table or 
    90 List) and as a result are responsible for labels and error display. Fields are 
    91 fairly self explanatory, you can get an overview of what widgets are available 
    92 by checking out the widgets browser in the new turbogears toolbox. Fastdata is 
    93 a grid that autogenerates forms based on the SQLObject column type. It's 
    94 useful for prototyping, but we won't be using it in this tutorial. 
    96 You must have your Fields in a Form in order to use the widgets framework:: 
    99 #!python 
    100   example_form = widgets.TableForm( 
    101         fields=[widgets.TextField(name="test",label="Example")], 
    102         submit_text="Submit Me") 
     3This wiki page has been migrated to the new docs site currently at  
    105 This proto-form needs to be put on the page by calling it with an action 
    106 argument in your template:: 
    108 {{{ 
    109 #!python 
    110   ${example_form(action="sample")} 
    111 }}} 
    113 Which will produce the following output:: 
    115 {{{ 
    116 #!text/html 
    117     <FORM ACTION="sample" NAME="form" METHOD="post"> 
    118         <TABLE BORDER="0"> 
    119             <TR> 
    120                 <TD> 
    121                     <LABEL CLASS="fieldlabel" FOR="test">Example</LABEL> 
    122                 </TD> 
    123                 <TD> 
    124                     <INPUT CLASS="textfield" TYPE="text" ID="test" NAME="test"> 
    125                 </TD> 
    126             </TR> 
    127             <TR> 
    128                 <TD> </TD> 
    129                 <TD> 
    130                 <INPUT TYPE="submit" CLASS="submitbutton" VALUE="Submit Me"> 
    131                 </TD> 
    132             </TR> 
    133         </TABLE> 
    134     </FORM> 
    135 }}} 
    137 If you want to preset the values - for instance to edit already existing data - use the following form: 
    139 {{{ 
    140 #!python 
    141   ${example_form(data, action="sample")} 
    142 }}} 
    144 with the data in the form of a dictionary: 
    146 {{{ 
    147 #!python 
    148   data = dict([('test', 'Hello World'),]) 
    149 }}} 
    151 The form strips off the submit field so that you don't have to deal with it. 
    152 If you do want to manage the submit button, add a `widget.Submit` to your 
    153 form. The default label is the capitalized widget name. Create a custom label 
    154 using the `label` argument. 
    156 Conversion from a python value to a string displayed as part of the form is 
    157 handed by a FormEncode validator, which is attached to the widget using the 
    158 named `validator` argument. More on validation later. 
    160 The function/constructor form syntax is a bit unweildy and doesn't look nice 
    161 on the screen, so a declarative syntax is provided and is used in this 
    162 project. To take advantage of this syntax, simply subclass 
    163 `widgets.WidgetsList`. Widgets declared this way will automatically 
    164 have their name set to the attribute name but are otherwise exactly as they 
    165 would be if you created them as shown above. When instantiated, the widgets 
    166 wind up as a simple python list. This means that you can do list-like things 
    167 when you put it in a form, such as adding additional widgets:: 
    169 {{{ 
    170 #!python 
    171   comment_form_2 = widgets.TableForm( 
    172       fields=CommentFields().append( 
    173           widgets.TextField(name='added') 
    174       ), 
    175       submit_text="Submit Tweaked Form" 
    176   ) 
    177 }}} 
    179 == Validation == 
    181 The `comment_form` has a validator on each of the first two fields. It's 
    182 fairly obvious that the first field (`validators.NotEmpty`)is required, but 
    183 the second field (`validators.Email`) is required as well. Adding a 
    184 validator to the field generally makes that field required, but you can get 
    185 around this by passing an `if_empty="default value"` argument to the 
    186 validator's constructor. Note that validators don't need to be instantiated 
    187 unless you're passing in arguments. 
    188 Each of the form's validators are rolled up into a form-wide 
    189 `FormEncode.Schema`, which is used to check the contents for validity by the 
    190 `@validate` method decorator. 
    192 Methods previously had to check for `cherrypy.form_errors` and re-dispatch 
    193 based on the result. The new `@error_handler` decorator makes handling this 
    194 error quite a bit simpler. The first argument to the decorator is the error 
    195 handling method. In the example, we're re-using `add` so that the form will 
    196 be re-displayed if errors occur. A simple addition would be to display a 
    197 message to the user indicating there was a problem:: 
    199 {{{ 
    200 #!python 
    201   @expose(template=".templates.form") 
    202   def add(self,tg_errors=None): 
    203       if tg_errors: 
    204           flash("There was a problem with the form!") 
    205       return dict( 
    206           form=comment_form, 
    207           action='save', 
    208           title='New Comment' 
    209       ) 
    210 }}} 
    212 In addition to redirecting the error, the `@error_handler` decorator will 
    213 look for a parameter named 'tg_errors' and will put error information into 
    214 that parameter. The decorator also has super RuleDispatch powers allowing you 
    215 to redirect errors based on arbitrarily complex conditions, but that is beyond 
    216 the scope of this tutorial. 
    218 == Form Processing == 
    220 As part of validation, the values of the form will be converted to the 
    221 appropriate python objects (this example is all strings, but if you used an 
    222 `Int` validator, for example, you'd get a python integer rather than a 
    223 string containing an integer). If you list the field names out 
    224 as arguments in the decorated function, as in the example, the values get put 
    225 into the appropriate arguments. If, on the other hand, you put a keyword 
    226 argument parameter, e.g.:: 
    228 {{{ 
    229 #!python 
    230   @expose() 
    231   @validate(form=comment_form) 
    232   @error_handler(add) 
    233   def save(self, **data): 
    234       comments.add(data['name'], 
    235                    data['email'], 
    236                    data.get('comment','No Comment.')) 
    237       #... 
    238 }}} 
    240 Then you get the data as a dictionary. The use of `.get()` above is needed for 
    241 the last two because those attributes aren't guaranteed to exist by the 
    242 validators, while the first two are. 
    244 The `flash` method displays a notice on the next page a user visits (and 
    245 only on the first page). 
    247 == Back to the Index == 
    249 And we conclude the tutorial where we began, back at the index. 
    251 TurboGears 0.9 comes jam-packed with new features and this tutorial only 
    252 covers the most basic and immediate changes. You'll probably want to explore 
    253 identity, the toolbox, and widgets in greater detail. 
    255 See you on the mailing list. 
    257   --Karl 
    259 Note: The files for this example are courtesy of Michele Cella, but I've tweaked them 
    260 a bit! 
    261 Note2: The example code doesn't work with turbogears 1.1a0 -- someone needs to update it.