Helper for Custom Forms

18 views
Skip to first unread message

rfx_labs

unread,
Dec 28, 2008, 10:53:13 AM12/28/08
to web2py Web Framework
Some treads here disus the handling of custom forms.

One hint was use SQLFORM in the controller and build anything else in
the view.

I don't like the idea to do things twice.

Another hint was {{=form[0][1][1][0]}}.
Not so handy and may break when fields are added to the table.
So i wrapped this in an function. Now I have both quick SQLForms for
development and all posiilites for make my one styled forms.

In a Form I can now do:
<ol>
{{=LI(form.elm("name"))}}
{{=LI(form.elm("match"))}}
</ol>
rendert as:
<li><label for="name">Name: </label><input class="string" id="name"
name="name" type="text" value="" /></li>
<li><label for="match">Match: </label><select class="reference match"
id="match" name="match"><option value="1">xyz</option></select></li>

or:
{{=form.elm("reference", _type="hidden")}}
{{=form.elm("id")}}{{=form.hidden_fields()}}
rendert as:
<input class="reference reference" id="reference" name="reference"
type="hidden" value="" />
<input name="_formkey" type="hidden" value="be9653b2-73ab-459b-b63d-
e3831198da3b" /><input name="_formname" type="hidden" value="color" />

or:
{{for elm in "cmyk":}}
{{=LI(form.elm(elm, SPAN(elm.capitalize()+": ",
_id="color_"+elm) ) )}}
{{pass}}
rendered as:
<li><label for="c"><span id="color_c">C: </span></label><input
class="integer" id="c" name="c" type="text" value="0" /></li>
<li><label for="m"><span id="color_m">M: </span></label><input
class="integer" id="m" name="m" type="text" value="0" /></li>
<li><label for="y"><span id="color_y">Y: </span></label><input
class="integer" id="y" name="y" type="text" value="0" /></li>
<li><label for="k"><span id="color_k">K: </span></label><input
class="integer" id="k" name="k" type="text" value="0" /></li>

Any suggestions are welcome.

Martin

Here's the code that I put in a model:
#patch SQLFORM
def __elm(self, elm, label=True, _id=True, **kargs):

if elm is "id":
#why is id not in form.hidden_fields()?
return self.components[1] if len(self.components) > 1 else ""
elif elm is "delete":
fid = -2
else:
fid = self.fields.index(elm)-1
if fid < 0: raise SyntaxError, "Field not in Form!"

field = self.components[0][fid][1][0]

if elm is "delete" and field.attributes["_id"] is not
"delete_record": return ""

if _id is True and "_name" in field.attributes:
#name as id, this is maybe a little to specific for only me
field.attributes["_id"] = field.attributes["_name"]
elif type(_id) is str:
#custom id
field.attributes["_id"] = _id

for key in kargs.keys():
field.attributes[key] = kargs[key]

if "_type" in field.attributes.keys() \
and field.attributes["_type"] is "hidden":
label=False

if label:
lb = self.components[0][fid][0][0]

if _id is True and "_id" in field.attributes:
lb.attributes["_for"] = field.attributes["_id"]
elif type(_id) is str:
lb.attributes["_for"] = _id

lb.attributes["_id"] = None

if label is not True:
lb.components[0] = XML(label)

field = XML(lb.xml() + field.xml())

return field

SQLFORM.elm = __elm

mdipierro

unread,
Dec 28, 2008, 11:36:22 AM12/28/08
to web2py Web Framework
Can you email me the code below? the indentation is messed up.

Massimo

mdipierro

unread,
Dec 28, 2008, 1:22:39 PM12/28/08
to web2py Web Framework
I really like your idea although there are some of the implemetation
issues too specific. I modified it slightly and this is what I have in
trunk:

>>> a=DIV('hello',DIV('world',_id='test'))
>>> print a.element(_id='test',_class=None).update(_class='junk').xml()
<div class='junk' id='test'>world</div>

any helper, including a SQLFORM now has a element(....) method that
can be used to search the first matching element.
any helper also has an update(...) method that can be used to update
the attributes and returns self.

This is not equivalent to your patch but it will make your simpler.

Hope it is acceptable.

Massimo

rfx_labs

unread,
Dec 28, 2008, 3:20:53 PM12/28/08
to web2py Web Framework
Hi Massimo,

> This is not equivalent to your patch but it will make your simpler.
I have tried your modifikation.
it does all what i need, without any magic anymore , that's great.

> Hope it is acceptable.
For me it is, hopefully this helps other too ;-)

Martin

Jon

unread,
Dec 28, 2008, 5:16:33 PM12/28/08
to web2py Web Framework
Martin,

If you use this successfully to build custom form(s) can you post a
sample of your controller and view here?

Thanks!

Jon

rfx_labs

unread,
Dec 28, 2008, 7:22:38 PM12/28/08
to web2py Web Framework
Jon,

> If you use this successfully to build custom form(s) can you post a
> sample of your controller and view here?

Model:
proof=SQLDB("sqlite://proof.db")

proof.define_table('match',
SQLField('name'),
SQLField('min', 'integer'),
SQLField('intent'),
SQLField('keep_black', 'boolean'))

proof.define_table('color',
SQLField('name'),
SQLField('match', proof.match),
SQLField('extra')
SQLField('c', 'integer', default=0),
SQLField('m', 'integer', default=0),
SQLField('y', 'integer', default=0),
SQLField('k', 'integer', default=0),
SQLField('publish', 'boolean')
SQLField('preview'))

proof.color.name.requires=IS_NOT_EMPTY()
proof.color.match.requires=IS_IN_DB(proof, "match.id", "%(name)s",
orderby=proof.match.pos)
proof.color.extra.requires=IS_IN_SET(("Custom", "Option 2", "Option
3"))
proof.color.c.requires=IS_INT_IN_RANGE(0,101)
proof.color.m.requires=IS_INT_IN_RANGE(0,101)
proof.color.y.requires=IS_INT_IN_RANGE(0,101)
proof.color.k.requires=IS_INT_IN_RANGE(0,101)

Controller:
def update():
try:
rid = int(request.args[0])
record = proof(proof.color.id==rid).select()[0]
except:
session.flash=T('Entry doesn't exists!')
redirect(URL(r=request,f='index'))

form=SQLFORM(proof.color, record, deletable=True)

if form.accepts(request.vars,session):
redirect(URL(r=request, f='index'))

return dict(form=form)

You see nothing special so long ;-)

view:
<form action="" enctype="multipart/form-data" method="post">
<fieldset>
<legend>Generic</legend>
<ol>
<li><label for="color_name">Name:</label>{{=form.element
(_name="name")}}</li>
<li><label for="my_fancy_id">Color-Match:</label>{{=form.element
(_name="match").update(_id="my_fancy_id")}}</li>
<li><label for="color_extra">Name:</label>{{=form.element
(_name="extra")}}</li>
</ol>
</fieldset>
<fieldset>
<legend>Color values</legend>
<ol>
{{for elm in "cmyk":}}
<li><label for="color_{{=elm}}"><span id="c_{{=elm}}">
{{=elm.capitalize()}}:</span></label>{{=form.element(_name=elm)}}</li>
<!-- or write this better with helpers
{{=LI(LABEL(SPAN(elm.capitalize()+":", _id="c_"+elm),
_for="color_"+elm), form.element(_name=elm))}}
-->
{{pass}}
</ol>
</fieldset>
<fieldset>
<label for=color_publish>publish</label>{{=form.element
(_name="publish")}}
<label for="delete_record">Delete this color</label>{{=form.element
(_id="delete_record")}}
<input type="submit" value="save">
{{=form.element(_name="reference").update(_type="hidden")}}
{{=form.element(_name="id")}}{{=form.hidden_fields()}}
</fieldset>
</form>

returns:
<form action="" enctype="multipart/form-data" method="post">
<fieldset>
<legend>Generic</legend>
<ol>
<li><label for="color_name">Name:</label><input class="string"
id="color_name" name="name" type="text" value="Cyan" /></li>
<li><label for="my_fancy_id">Color-Match:</label><select
class="reference match" id="my_fancy_id" name="match"><option
selected="selected" value="1">Match 1</option><option value="2">Match
2</option></select></li>
<li><label for="color_extra">Name:</label><select class="string"
id="color_extra" name="extra"><option value="Custom">Custom</
option><option value="Option 2">Option 2</option><option value="Option
3">Option 3</option></select></li>
</ol>
</fieldset>
<fieldset>
<legend>Color values</legend>
<ol>

<li><label for="color_c"><span id="c_c">C:</span></label><input
class="integer" id="color_c" name="c" type="text" value="0" /></li>
<!-- or write this better with helpers
<li><label for="color_c"><span id="c_c">C:</span></label><input
class="integer" id="color_c" name="c" type="text" value="0" /></li>
-->

<li><label for="color_m"><span id="c_m">M:</span></label><input
class="integer" id="color_m" name="m" type="text" value="28" /></li>
<!-- or write this better with helpers
<li><label for="color_m"><span id="c_m">M:</span></label><input
class="integer" id="color_m" name="m" type="text" value="28" /></li>
-->

<li><label for="color_y"><span id="c_y">Y:</span></label><input
class="integer" id="color_y" name="y" type="text" value="100" /></li>
<!-- or write this better with helpers
<li><label for="color_y"><span id="c_y">Y:</span></label><input
class="integer" id="color_y" name="y" type="text" value="100" /></li>
-->

<li><label for="color_k"><span id="c_k">K:</span></label><input
class="integer" id="color_k" name="k" type="text" value="0" /></li>
<!-- or write this better with helpers
<li><label for="color_k"><span id="c_k">K:</span></label><input
class="integer" id="color_k" name="k" type="text" value="0" /></li>
-->

</ol>
</fieldset>
<fieldset>
<label for=color_publish>publish</label><input class="boolean"
id="color_publish" name="publish" type="checkbox" value="on" />
<label for="delete_record">Delete this color</label><input
class="delete" id="delete_record" name="delete_this_record"
type="checkbox" value="on" />
<input type="submit" value="save">
<input class="reference reference" id="color_reference"
name="reference" type="hidden" value="5" />
<input name="id" type="hidden" value="4" /><input name="_formkey"
type="hidden" value="815a1592-f25e-4a25-af72-78ebedced385" /><input
name="_formname" type="hidden" value="color" />
</fieldset>
</form>

I hope you get an idea.

The benefits:
- only once define requires for appadmin and your custom forms in the
model
- handling of field values and errors is done from SQLFORM()
- No need to adjust class or id in controller


Martin

Jon

unread,
Dec 28, 2008, 9:17:53 PM12/28/08
to web2py Web Framework
I got it working, thanks! Only question: what does the 'returns:' part
of your update.html do? That part seemed hard-coded for "Cyan" and I
couldn't figure out its purpose. Thanks!

Jon

unread,
Dec 28, 2008, 9:36:12 PM12/28/08
to web2py Web Framework
One other question: it kept saying this function was invalid
(.update):

{{=form.element(_name="reference").update(_type="hidden")}}

Can you tell me if the syntax is wrong? I see the value in making the
type of some form elements "hidden", etc...!

Jon


On Dec 28, 6:22 pm, rfx_labs <l...@reproflex.de> wrote:

mdipierro

unread,
Dec 29, 2008, 1:00:16 AM12/29/08
to web2py Web Framework
in

form.element(_name="reference").update(_type="hidden")

the update is invalid if the element returns None because no TAG has
_name='reference'.

Massimo

rfx_labs

unread,
Dec 29, 2008, 4:39:46 AM12/29/08
to web2py Web Framework
Hi Jon,

> form.element(_name="reference").update(_type="hidden")
I'am sorry. I've copied the example from a real application. But
simplfy the model, and forget to adjust the name of deleted field
reference in the view.

>'returns:
is the rendert output of the view ,-)


Martin

Jon

unread,
Dec 29, 2008, 11:03:11 AM12/29/08
to web2py Web Framework
Thanks all. This seems very useful indeed and less cumbersome than
making all forms with the FORM process + view set-up.
Reply all
Reply to author
Forward
0 new messages