.. testsetup:: * import kajiki import ew .. testsetup:: resource import kajiki import ew Template=kajiki.XMLTemplate(filename='data/master.html') class c: pass class g: pass EasyWidgets Tutorial ======================================= Intro -------- EasyWidgets is a minimalistic reimplementation of TurboGears widgets. It does not, however, depend on TurboGears being installed in order to be useful. A widget provides one or more of the following features, all bundled together: * HTML markup (via a templating engine) * Validation (for forms) * Static resources to be injected into the rendered HTML page This tutorial will demonstrate how to integrate widgets into a simple TurboGears application as well as how to create your own widgets. Prerequisites ------------------- EasyWidgets is designed to work with Python 2.6 or higher and the excellent FormEncode form validation library. To install EasyWidgets, you can use pip or easy_install:: easy_install EasyWidgets pip install EasyWidgets You should also install a templating language such as kajiki or jinja2 (also available via pip or easy_install). Your First Widget ----------------------- The most basic widget does absolutely nothing and has no visible representation. This is boring, so let's do a simple "hello, world" style widget: >>> import ew >>> w = ew.Widget(template=ew.Snippet('Hello, world!')) >>> w.display() literal(u'Hello, world!') Two things to note here. First is that widgets can be configured to use either snippets (python strings containing template text) or files (filenames of templates on disk). You can also specify a template engine to use for your template. In this case, we omitted the template engine name and thus are using the core-ew engine, which provides simple expression substitution. .. note:: Template Engines You can also use one of the following engines, based on which template languages you have installed: json render a jsonified dictionary core-ew basic expression substitution using ${...} genshi Genshi templating engine jinja2 Jinja2 templating engine kajiki Kajiki templating engine, with output type autodetect kajiki-text Kajiki text templating kajiki-html4 Kajiki html4 templating kajiki-html5 Kajiki html5 templating kajiki-xml Kajiki xml/xhtml templating Many of the examples in this tutorial will use the Kajiki templating engine, though they should be trivially translatable to other engines. The second thing to note in the example above is the use of the `literal` object to wrap the output of the template. This is the `webhelpers.html.literal` subclass of `unicode`, and provides an `__html__` method which is used by many web frameworks to mark the strings as html-safe. (Since templates are used for rendering html, they are safe to embed without further escaping into your output page.) Let's add a tiny bit of interactivity to the template by using a variable: >>> w = ew.Widget(template=ew.Snippet('Hello, ${name}!')) >>> w.display(name=None) literal(u'Hello, None!') >>> w.display(name='Rick') literal(u'Hello, Rick!') Here we see that we can customize the output of the widget by passing in variables. But that "Hello, None" is ugly. Wouldn't it be better to use a default value? Let's try that, in the process looking at an alternative method for defining templates that you'll probably use a good bit more than ew.Widget: >>> class Hello(ew.Widget): ... template=ew.Snippet('Hello, $name') ... defaults=dict(name='World') ... >>> Hello().display() literal(u'Hello, World') >>> Hello(name='Rick').display() literal(u'Hello, Rick') >>> Hello(name='Rick').display(name='Paul') literal(u'Hello, Paul') Here, we see a couple of things. First, we see the declarative widget definition. We can declare a ew.Widget subclass that provides additional functionality. Using the declarative method, we are also able to provide defaults to the widget via the `defaults` class variable. You should also note that the defaults can be overridden in either the constructor or the `display` method, with `display` being the highest priority, followed by the constructor, followed by the defaults. Form Generation and Validation --------------------------------------------------- Although reusable html generation is useful in its own right, most templating languages already provide constructs to support this. Where EasyWidgets begins to be *really* useful is when you link form generation and validation together. To that end, EasyWidgets provides several base form classes that you can use to build and validate your forms. Here is a sample form: .. testcode:: import ew.kajiki_ew as kew class MyForm(kew.SimpleForm): fields = [ kew.TextField(name='name', label='Your Name'), kew.Checkbox(name='opt_in', label='Receive awesome offers?') ] f = MyForm() print f.display() If we run this, our output will be similar to the following: .. testoutput:: :options: +NORMALIZE_WHITESPACE
Again, nice that EasyWidgets provides HTML generation, but what about validation? >>> f.validate(dict(name='Some Name', opt_in='on'), None) {'opt_in': True, 'name': u'Some Name'} >>> f.validate(dict(name='Some Name'), None) {'opt_in': False, 'name': u'Some Name'} Note in particular how the 'on' value, as might be submitted by an HTML form, is converted to the Python `True` value. Omitting it, rather than causing the field to be omitted from the output, substitutes a `False` Value. Form validation also works in reverse: .. testcode:: print f.display(value=dict(name='Other Name', opt_in=True)) .. testoutput:: :options: +NORMALIZE_WHITESPACE Note in the above output that the values for 'name' and 'opt_in' have been substituted into the form appropriately. This is especially useful when there are validation errors. Let's update our form just a bit: >>> from formencode import validators as fev >>> class MyForm2(kew.SimpleForm): ... fields = [ ... kew.TextField( ... name='name', ... label='Your Name', ... validator=fev.UnicodeString(min=3)), ... kew.Checkbox( ... name='opt_in', ... label='Receive awesome offers?') ] ... ... >>> f = MyForm2() Here, we have added an explicit validator to our `name` field, requiring that the name be at least 3 characters long. When trying to validate bad data, we get: >>> f.validate(dict(name='a'), None) Traceback (most recent call last): ... Invalid: name: Enter a value 3 characters long or more That's nice, but best practices for the web say you should present your user's erroneous input to them and allow them to correct it. Which is exactly what happens if we display a form after receiving a validation error: .. testcode:: print f.display() .. testoutput:: :options: +NORMALIZE_WHITESPACE Note in particular that the error message from our validator is nicely arranged next to the field with the erroneous value, and that the data the user entered is redisplayed to allow the user to correct the form. Including Static Resources ---------------------------------------------------- In addition to encapsulating HTML generation and validation, EasyWidgets also provides a convenient method of including static resources such as CSS and Javascript in your pages automatically when using widgets. In order to use this feature effectively, you will need to make a couple of changes to the main templates you use (not the widget templates) to generate your site HTML. For instance, if you are using Kajiki, you may have a `master.html` template from which all your page templates inherit that looks something like this: .. code-block:: xml