[Twisted-web] Formless, custom form layout

Valentino Volonghi aka Dialtone dialtone at divmod.com
Thu Jun 30 13:28:24 MDT 2005


Ian Bicking wrote:

> I'm proposing that validation and form generation be *completely*
> seperate.  So you get several real data structures.  The validation
> produces something like this:
>
>   {'field_name': 'field_value', ...}
>   {'field_name': 'error_string', ...}

That's how forms works. Perhaps I missed some bits of my reasoning.
The validation result is exactly a dict of

{'field_name': 'field_value'..}

passed to the method which signature is:

def callback(self, ctx, form, data)

and data is the dict you want.
The same is for the error 'dict', but you won't use that since it will
be used
during re-rendering of the page with error messages.

> You can package that in an object or whatever, but that's the basic
> data.  Then the form generation produces:
>
>   <input type="text" name="field_name">
>
right

> Then a third component (like htmlfill) puts these two items together,
> but does so with no knowledge of how the data came into being.  And
> the actual logic for redisplaying forms and whatnot should be entirely
> seperate, because
>
I guess htmlfill role is played by FormRenderer which is the one that
collects all the form fields and renders each of them.

> Traditionally form toolkits generate HTML widgets with values and
> errors  already filled in.  This binds HTML generation to the
> validation process, which almost always falls apart eventually,
> because no one HTML generator is general enough (and the most general
> HTML generator -- an actual person writing HTML by hand -- should
> always be allowed for).

Currently forms doesn't 'support' form customization. Right now it
renders itself over this pattern:

        T.form(id=T.slot('id'), action=T.slot('action'),
class_='nevow-form', method='post', enctype='multipart/form-data',
**{'accept-charset':'utf-8'})[
            T.input(type='hidden', name='_charset_'),
            T.slot('errors'),
            T.slot('items'),
            T.div(id=T.slot('fieldId'), pattern='item',
_class=T.slot('class'))[
                T.label(_for=T.slot('id'))[T.slot('label')],
                T.div(_class='inputs')[T.slot('inputs')],
                T.slot('description'),
                T.slot('message'),
                ],
            T.div(class_='hiddenitems')[
                T.slot('hiddenitems'),
                T.invisible(pattern="hiddenitem")[T.slot('inputs')]
                ],
            T.div(class_='actions')[
                T.slot('actions'),
                ],
            ]


The div with pattern="item" is the single slot. message slot is the
error message.
The FormRenderer is the thing that fills those slot taking them from the
right place (it is the controller in an MVC view).

> Maybe form is doing this, but at least it doesn't sound like it is. 
> But it does sound like it uses two-way conversion/validation, which I
> think is also a very important feature.

Yep. Particularly I'd like to show you the interfaces involved.

class IType(Interface):
    def validate(self, value):
        pass

class IWidget(Interface):
    def render(self, ctx, key, args, errors):
        pass
   
    def processInput(self, ctx, key, args):
        pass
       
class IConvertible(Interface):
    def fromType(self, value):
        pass
    def toType(self, value):
        pass
       
class IStringConvertible(IConvertible):
    pass

# other Convertibles

-- 
Valentino Volonghi aka Dialtone
Now Running MacOSX 10.4.1
Blog: http://vvolonghi.blogspot.com
http://weever.berlios.de




More information about the Twisted-web mailing list