The Almighty Form

183 views
Skip to first unread message

Alfonso Serra

unread,
Dec 16, 2015, 4:53:16 AM12/16/15
to web...@googlegroups.com
Im trying to create a formstyle, when the form is submitted without introducing any value it skips any kind of validation and, without being accepted, tries to perform db changes. Eventually i get an error ticket like:

pymysql.err.InternalError'> (1048, u"Column 'salida' cannot be null")


This is the style:

def formstyle_stardom(form, fields):

    parent
= CAT()

   
for fieldname in form.fields:
        field
= form.table[fieldname]

       
if field.type != "id":
            parent
+= LABEL(fieldname, _for=fieldname) + INPUT(_name=fieldname, value=field.default)
   
    parent
+= INPUT(_type="submit")
   
return parent

response
.formstyle = formstyle_stardom


at the controller:

form = SQLFORM(db.reservas).process()


at the view:

{{=form}}


Nothing special,

im not using "for id, label, control, help in fields:" since i would like to build the controls, but if i use the parameter "form" to create custom markup, the form will skip validation. When i use the "fields" parameter the form behaves correctly.


The question is, if the form is passed as a parameter why cant we use it to create the style? Am i missing something? How is it posible that the form skips validation?


Thanks.


Alfonso Serra

unread,
Dec 16, 2015, 9:14:02 AM12/16/15
to web2py-users
Ive found a workaround but the question still remains. Why cant we define formstyles like the example above?

the workaround is by creating the formstyle like this:

def mystyle(form):
    container
= CAT()
   
for fld in form.fields:
        container
+= INPUT(_name=fld)
    container
+= INPUT(_type="submit")

   
return CAT(form.custom.begin, container, form.custom.end)

at the view:
{{=mystyle(form)}}

I can go on with my project but i would like to use web2py features instead.
If form styles would work as expected the project would be easier to mantain.

Niphlod

unread,
Dec 16, 2015, 2:53:26 PM12/16/15
to web2py-users
you're doing it wrong. "fields" is a tuple containing - usually -  id, label, controls and help.

Anthony

unread,
Dec 16, 2015, 4:37:39 PM12/16/15
to web2py-users
On Wednesday, December 16, 2015 at 4:53:16 AM UTC-5, Alfonso Serra wrote:
Im trying to create a formstyle, when the form is submitted without introducing any value it skips any kind of validation and, without being accepted, tries to perform db changes. Eventually i get an error ticket like:

pymysql.err.InternalError'> (1048, u"Column 'salida' cannot be null")


This is the style:

def formstyle_stardom(form, fields):

    parent
= CAT()

   
for fieldname in form.fields:
        field
= form.table[fieldname]

       
if field.type != "id":
            parent
+= LABEL(fieldname, _for=fieldname) + INPUT(_name=fieldname, value=field.default)

If you're going to build everything completely from scratch, then you are responsible for adding the validators to the form controls. So, you need something like:

    INPUT(_name=fieldname, value=field.default, requires=field.requires)

If you use the "controls" object passed into the formstyle function as part of the "fields" object, this is done for you automatically.

Note, if you want custom widgets, a better approach might be to create custom widget functions/classes and use the "widget" argument to Field() when defining your models.

Anthony

Alfonso Serra

unread,
Dec 16, 2015, 5:09:51 PM12/16/15
to web...@googlegroups.com
Thanks Niphlod and Anthony for your answers.

Niphlod i didnt want to use the tuple fields, was using form.fields to build everything from scratch as Anthony said.

I like to have some kind of freedom to create html, without breaking internals whenever is posible.
I thought requires was performed in a different process than the form serialization. It would be better if the requires information isnt embeded in the inputs, but the form itself.
Wasnt expecting to redeclare requires as they already are at the models. In any case, that would do the trick.

Thanks for the help as i havent seen this in the documentation.

Anthony

unread,
Dec 16, 2015, 5:46:35 PM12/16/15
to web2py-users
On Wednesday, December 16, 2015 at 5:09:51 PM UTC-5, Alfonso Serra wrote:
I thought requires was performed in a different process than the form serialization. It would be better if the requires information isnt embeded in the inputs, but the form itself.
Wasnt expecting to redeclare requires as they already are at the models. In any case, that would do the trick.

Note, SQLFORM is an abstraction on top of FORM, so for validators to work with FORM (which does not involve a DAL model), it must be possible to define them in association with individual inputs within the form. That is the reason for the current architecture. SQLFORM simply takes a DAL model and uses it to create a FORM object -- from that point, all the validation happens via the FORM object, which knows nothing about DAL models or DAL Field objects. The alternative would require writing separate validation logic for SQLFORM rather than relying on what is already available via FORM.

Anthony

Alfonso Serra

unread,
Dec 17, 2015, 8:07:44 AM12/17/15
to web...@googlegroups.com
Makes sense, but now i have automatic markup in my view when the form has errors, which is something that im trying to avoid.
Ive discovered in html.py code that i can use hideerror=True in all inputs to prevent that.

Thanks

Anthony

unread,
Dec 17, 2015, 11:03:52 AM12/17/15
to web2py-users
If you really want to handle all of your HTML manually, don't bother using the web2py form object in the view at all. Just use SQLFORM for the server-side processing (i.e., validation and database inserts/updates). The only two requirements for the form HTML are that the "name" attributes of the inputs match what SQLFORM is expecting (which are the DAL field names) and that you include {{=form.hidden_fields()}} somewhere in the form (this includes the hidden _formname and _formkey fields, which are used for CSRF protection). To display validation errors, you can access the errors in form.errors.

Anthony

Alfonso Serra

unread,
Dec 17, 2015, 1:30:06 PM12/17/15
to web...@googlegroups.com
I thought about it but it has a disadvantage. The SQLFORM is perfect to render complex queries without having to hardcode the html. If the models changes i dont have to worry about the view.

And this is where the bootstrap decouple comes in place. Everything is easy if you use the welcome app as layout but for custom markup there are not many examples on how to do things.
I would love to be able to solve these small things without bothering you guys too much.
Once i have knowledge enough on how to build a site without any automatic markup, im planning to make video tutorials.

If its not too much trouble i have another question:

The form style isnt aware of form errors so i cannot "style" the form properly.
When i print form.errors inside the style i get an empty storage even when there are errors.

Is there any particular way to achieve this, using formstyles?

Thanks.

Anthony

unread,
Dec 17, 2015, 2:10:39 PM12/17/15
to web2py-users
On Thursday, December 17, 2015 at 1:30:06 PM UTC-5, Alfonso Serra wrote:
I thought about it but it has a disadvantage. The SQLFORM is perfect to render complex queries without having to hardcode the html. If the models changes i dont have to worry about the view.

You don't necessarily have to hard-code the HTML -- you could write your own abstraction that takes either a web2py FORM object or a DAL table model and generates the particular HTML you need for your forms. Once you have created this abstraction, you could re-use it for all forms.
 
And this is where the bootstrap decouple comes in place. Everything is easy if you use the welcome app as layout but for custom markup theres not many examples on how to do things.

Just to be clear, nothing is coupled to Bootstrap (in fact, both FORM and SQLFORM pre-date Bootstrap). web2py provides several formstyles, including a couple that work specifically with Bootstrap. If you want to deviate from the built-in formstyles, yes, it will be more work, but that is unavoidable, as web2py cannot anticipate every possible way someone might want to structure the markup for a form. It's not reasonable to expect completely automatic UI generation in conjunction with customized UI requirements.
 
If its not too much trouble i have another question:

The form style isnt aware of form errors so i cannot "style" the form properly.
When i print form.errors inside the style i get an empty storage even when there are errors.

Is there any particular way to achieve this, using formstyles?

Not directly with formstyles, as the formstyle is used to generate the form DOM before validation. The errors aren't added until the form object is serialized into HTML (i.e., when the .xml method is called). If you don't like the errors automatically added to the form, you should instead hide the errors, and then you could add your own via server-side DOM manipulation.

Anthony

Alfonso Serra

unread,
Dec 18, 2015, 3:14:38 AM12/18/15
to web...@googlegroups.com
you should instead hide the errors, and then you could add your own via server-side DOM manipulation.

But how do i do that if i dont know when errors has happened?.

Currently the form gets automatic markup when is instantiated and its modified later on when process is called.
But this is like executing the view before the controller.
Wouldnt be better to serialize the form just once, when xml() is called?, even perfomance wise since nothings gets serialized until its droped into a view.

Either i have to go back to my workaround {{=mystyle(form)}} or rearrange the SQLFORM code so gets serialized at the end of its lifespan, like it should, in my opinion.

So far ive overriden "def xml(self):" to something like
return form.custom.begin +  self.createform(xfields=None) + form.custom.end

Ive to deal with some issues like xfields but i hope it will work.
I appreciate your opinion about this matter.

Thanks.



Anthony

unread,
Dec 18, 2015, 9:23:48 AM12/18/15
to web2py-users
On Friday, December 18, 2015 at 3:14:38 AM UTC-5, Alfonso Serra wrote:
you should instead hide the errors, and then you could add your own via server-side DOM manipulation.

But how do i do that if i dont know when errors has happened?.

After validation, if there are errors, then you will find them in form.errors.
 
Currently the form gets automatic markup when is instantiated and its modified later on when process is called.
But this is like executing the view before the controller.
Wouldnt be better to serialize the form just once, when xml() is called?, even perfomance wise since nothings gets serialized until its droped into a view.

The form is only serialized into HTML once -- when you do {{=form}} in the view. The server-side DOM of the form is created when the form is instantiated, but it is not serialized to HTML at that point. The reason the DOM is created when the form is instantiated is so validators (and other attributes) can be attached to the appropriate elements when the form is defined. This is necessary with FORM because there is no DAL model associated with the form, so the form elements themselves must store these attributes. Because SQLFORM is built on top of FORM, it uses the same mechanism.
 
Either i have to go back to my workaround {{=mystyle(form)}} or rearrange the SQLFORM code so gets serialized at the end of its lifespan, like it should, in my opinion.

The problem is not that the form gets serialized before the view is executed (it doesn't), but that the errors are added during the serialization process, so there is no opportunity to customize them (short of excluding them altogether and adding your own errors to the DOM after validation but before serialization). This should be improved.
 
So far ive overriden "def xml(self):" to something like
return form.custom.begin +  self.createform(xfields=None) + form.custom.end

I'm not sure it's worth bothering with that. If you need completely custom markup, the {{=mystyle(form)}} solution is a reasonable approach.

Anthony

Alfonso Serra

unread,
Apr 26, 2017, 5:18:15 PM4/26/17
to web2py-users
This is great i have found a way to have full control on how a form is displayed and behave other than writing mystyle(form) in a view.

This is how it would look like:
def stylefrm(self):
   
#create any custom header or form.custom.begin
    frm
= FORM(_class="myform")
   
   
#iterate over the fields and create any behaviour and style you like
   
   
for fld in self.fields:
        field
= self.table[fld] #access the SQLFORM to inspect field properties
       
       
#implement keepvalues
       
if self.keepvalues:
           
out = LABEL(fld, _for=fld) + INPUT(_name=fld, requires=field.requires, _value=self.vars[fld])
       
else:
           
out = LABEL(fld, _for=fld) + INPUT(_name=fld, requires=field.requires)
           
        frm
.append(out)
   
   
# add the hidden fields + token    
    frm
.append(self.hidden_fields())
   
   
#add any submit button
    frm
.append(INPUT(_type="submit", _value="submit"))
   
   
return str(frm)

# replace web2py html serialization for your own
SQLFORM
.xml = stylefrm

def index():
    frm
= SQLFORM.factory(
       
Field("name", "string")
       
, Field("quantity", "integer")
       
, Field("price", "double")
   
)
   
# remove SQLFORM factory auto id field
    frm
.fields.pop(0)
   
    result
= "Not submitted"

   
if frm.process(keepvalues=True).accepted:
        result
= "All good"
   
elif frm.errors:
        result
= "Not good"
   
   
return locals()

View:
{{=frm}}
{{=result}}

Carlos Costa

unread,
Apr 26, 2017, 7:07:56 PM4/26/17
to web...@googlegroups.com
Great tip.

I did something similar, but in views.
I wrote a function to take a form and break it into a giver number of columns using bootstrap grid classes.
The default stacked layout gets pretty boring with time.

--
Resources:
- http://web2py.com
- http://web2py.com/book (Documentation)
- http://github.com/web2py/web2py (Source code)
- https://code.google.com/p/web2py/issues/list (Report Issues)
---
You received this message because you are subscribed to the Google Groups "web2py-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to web2py+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--


Carlos J. Costa
Cientista da Computação  | BS Computer Science
Esp. Gestão em Telecom   |
PgC Telecom Mangement
<º))><
Reply all
Reply to author
Forward
0 new messages