"required" class for required fields

187 views
Skip to first unread message

mr.freeze

unread,
Aug 24, 2013, 10:36:16 AM8/24/13
to web...@googlegroups.com
I want to show users which fields are required *before* they submit a form. Before I reinvent the wheel, is there a mechanism for adding a "required" class to fields that have IS_NOT_EMPTY or required=True already built into web2py?

Thanks,
Nathan

Massimo Di Pierro

unread,
Aug 24, 2013, 5:29:47 PM8/24/13
to web...@googlegroups.com
You could use something like:

    db.table.field.comment = 'required'

or 

for field in db.table:
   if field.required:
       field.comment='required'

mr.freeze

unread,
Aug 24, 2013, 6:37:48 PM8/24/13
to web...@googlegroups.com
I want to take advantage of bootstrap's form validation classes so I need to add a class to the field. Too bad there's not a hook into when a Field's widget is rendered so you can manipulate it. Would you take a patch for that?  I can work around it with your method in the meantime:
for t in db.tables:
   
for f in db[t].fields:
       
if db[t][f].required:
            db
[t][f].comment = SCRIPT('jQuery("#%s_%s").attr("required","")' % (t,f))

Massimo Di Pierro

unread,
Aug 24, 2013, 7:22:22 PM8/24/13
to web...@googlegroups.com
Nice idea. But why put it in a comment?

script = ''
for table in db:
    
for field in table:
        
if field.required:
            script
 += 'jQuery("#%s_%s").attr("required","")' % (field._tablename,field.name)

You can then cache the script and place in the layout.

Massimo

mr.freeze

unread,
Aug 24, 2013, 7:43:47 PM8/24/13
to
Ideally, I wouldn't set the class via a script, that was just a hack.  I would like to alter the field after its default widget is rendered. I have a patch that allows you to do this. Thoughts?:
def add_required(elm):
    elm
['_class'] += ' required'
db
.mytable.myfield.onrender = add_required

mr.freeze

unread,
Aug 25, 2013, 1:35:28 AM8/25/13
to web...@googlegroups.com
Here's the patch. I adds an onrender property to Field which is called from the widget method of all default widgets.


On Saturday, August 24, 2013 6:42:49 PM UTC-5, mr.freeze wrote:
Ideally, I wouldn't set the class via a script, that was just a hack.  I would like to alter the field after its default widget is rendered. I have a patch that allows you to do this. Thoughts?:
def add_required(elm):
    elm
['_class'] += ' required'
db
.mytable.myfield.onrender = add_required



Field.onrender.patch

Anthony

unread,
Aug 25, 2013, 10:04:20 AM8/25/13
to web...@googlegroups.com

mr.freeze

unread,
Aug 25, 2013, 10:36:45 AM8/25/13
to web...@googlegroups.com
Thanks Anthony.  This is good for a specific use case but my patch would allow for arbitrary modification of a field's default widget output.  I think this could be generally useful. 

On Sunday, August 25, 2013 9:04:20 AM UTC-5, Anthony wrote:
Check out http://dev.s-cubism.com/plugin_notemptymarker.

Anthony

Niphlod

unread,
Aug 25, 2013, 10:51:30 AM8/25/13
to web...@googlegroups.com
-1 on the patch.

I don't really get it. Why do you (and all of us) need yet another attribute (too many already) of the Field when we have widget= for it ?
Is it really that hard to do

def my_string_widget(field, value):
   
return INPUT(_name=field.name, _id="%s_%s" % (field._tablename, field.name),
    _class
="whatever",
    _value
=value,
    _required
=""
    requires
=field.requires)

Field('comment', 'string', widget=my_string_widget)

that enables you to do all sorts of crazy things with your own widget ?!

mr.freeze

unread,
Aug 25, 2013, 11:38:24 AM8/25/13
to
Creating custom widgets is exactly what I'm trying to get around. I have a bunch of fields that just need a css class added.  The default SQLFORM widgets work perfectly otherwise.  So instead of creating a bunch of custom widgets I can just tweak the default widgets output for ALL fields like this:
def add_required(elm):
    elm
['_class'] += ' required'
for t in db.tables:
   
for f in db[t].fields:
       
if db[t][f].required
:
           
db[t][f].onrender = add_required
There is currently no hook into the rendering of default widgets otherwise. I hope that makes sense.

Niphlod

unread,
Aug 25, 2013, 11:53:16 AM8/25/13
to web...@googlegroups.com
Ok, the thing is that there are no hooks in rendering cause all the rendering is meant to be happen in your own widget.
Also, what you're trying to achieve works for input, but not for selects, list:string, etc etc. i.e. the method is only applicable if you know what widget is being used beforehand...more on the matter, it saves typing just the "required" attribute, that is somewhat "universal", that can really be addressed with the same for loop on a dict full of your own widgets.

In any case, with your patch you just added the code you needed, it doesn't save you any typing if you import you own widgets overwriting the default ones with your own.
Again, I'm not seeing a big improvement vs the added complexity.


On Sunday, August 25, 2013 5:37:15 PM UTC+2, mr.freeze wrote:
Creating custom widgets is exactly what I'm trying to get around. I have a bunch of fields that just need a css class added.  The default SQLFORM widgets work perfectly otherwise.  So instead of creating a bunch of custom widgets I can just tweak the default widgets output for ALL fields like this:
def add_required(elm):
    elm
['_class'] += ' required'
for t in db.tables:
   
for f in db[t].fields:
       
if db[t][f].required:
            db
[t][f].onrender = add_required
There is currently no hook into the rendering of default widgets otherwise. I hope that makes sense.


On Sunday, August 25, 2013 9:51:30 AM UTC-5, Niphlod wrote:

mr.freeze

unread,
Aug 25, 2013, 12:45:21 PM8/25/13
to web...@googlegroups.com
"Ok, the thing is that there are no hooks in rendering cause all the rendering is meant to be happen in your own widget." - I disagree.  You can modify a SQLFORM after it renders. I am simply trying to achieve a similar effect at the Field level.
"Also, what you're trying to achieve works for input, but not for selects, list:string, etc etc. i.e." - It is working for me in inputs, selects, etc. Did you try it out?
"In any case, with your patch you just added the code you needed, it doesn't save you any typing if you import you own widgets overwriting the default ones with your own." - Recreating all of the default widgets is a lot of typing
"Again, I'm not seeing a big improvement vs the added complexity." - The patch is very simple. It just calls an onrender method if it exists after a default widget is rendered.

Niphlod

unread,
Aug 25, 2013, 3:07:10 PM8/25/13
to web...@googlegroups.com


On Sunday, August 25, 2013 6:45:21 PM UTC+2, mr.freeze wrote:
"Ok, the thing is that there are no hooks in rendering cause all the rendering is meant to be happen in your own widget." - I disagree.  You can modify a SQLFORM after it renders. I am simply trying to achieve a similar effect at the Field level.
"Also, what you're trying to achieve works for input, but not for selects, list:string, etc etc. i.e." - It is working for me in inputs, selects, etc. Did you try it out?

yes, and it doesn't work for list:string and list:integer fields
 
"In any case, with your patch you just added the code you needed, it doesn't save you any typing if you import you own widgets overwriting the default ones with your own." - Recreating all of the default widgets is a lot of typing

for mods like this, it's just copy/paste. For heavier mods, you'll have to separate your "onrender" to be compatible with the different logics, that will save no typing at all and will be much more error prone.
 
"Again, I'm not seeing a big improvement vs the added complexity." - The patch is very simple. It just calls an onrender method if it exists after a default widget is rendered.


And it will hog down any Field with yet another "not-so-useful" property from now on, plus a check if onrender exists for every serialized widget out there (so, at least twice for any form submitted, for every field in the form).

Anyway, I'm starting to see a little bit of "grudge". I'll stop posting: I'm just stating that in my POV this should belong either to a formstyle or in your own widgets, so it's not worth the inclusion in web2py.

mr.freeze

unread,
Aug 25, 2013, 3:13:49 PM8/25/13
to web...@googlegroups.com
After using this, I am convinced it is generally useful.  Otherwise, I have to create custom widgets for each field.  I cleaned up the patch and submitted it here: http://code.google.com/p/web2py/issues/detail?id=1648

mr.freeze

unread,
Aug 25, 2013, 3:31:09 PM8/25/13
to web...@googlegroups.com
No grudge here.  Just making my case for what I consider to be a useful patch. The overhead should be nominal since it only adds a null check and moves on (premature optimization is the root of all evil).  Copy/paste is bad for code reuse. As the default widgets improve over time, the copied/pasted code would need to be maintained separately.  list:integer and list:string work like a champ for me:
def add_required(elm):
    elm
['_required'] = ''
form
= SQLFORM.factory(Field('age','list:integer', required=True,
                                 requires
=IS_IN_SET([12,23,34,45,56]),
                                 onrender
=add_required ),
                           
Field('colors','list:string', required=True,
                                 requires
=IS_IN_SET(['red','blue','green','orange','black']),
                                 onrender
=add_required ))



On Sunday, August 25, 2013 2:07:10 PM UTC-5, Niphlod wrote:

Niphlod

unread,
Aug 25, 2013, 4:24:46 PM8/25/13
to web...@googlegroups.com
try to remove the requires=IS_IN_SET...

Joe Barnhart

unread,
Aug 25, 2013, 4:34:40 PM8/25/13
to web...@googlegroups.com
I just discovered that dictform and smartdictform override my "requires" and add their own, based on the type of the Field.  It somewhat defeats the purpose of having a custom widget.

-- Joe

mr.freeze

unread,
Aug 25, 2013, 4:52:46 PM8/25/13
to web...@googlegroups.com
It still works fine for me after removing IS_IN_SET.  What are you seeing?

mr.freeze

unread,
Aug 25, 2013, 5:41:11 PM8/25/13
to web...@googlegroups.com
BTW, I don't think you would ever use list:string or list:integer without IS_IN_SET or IS_IN_DB. From the book:
"While list:reference has a default validator and a default representation, list:integer and list:string do not. So these two need an IS_IN_SET or an IS_IN_DB validator if you want to use them in forms."

Alan Etkin

unread,
Aug 25, 2013, 6:05:37 PM8/25/13
to web...@googlegroups.com

"Ok, the thing is that there are no hooks in rendering cause all the rendering is meant to be happen in your own widget." - I disagree.  You can modify a SQLFORM after it renders. I am simply trying to achieve a similar effect at the Field level.


How about a Field(..., attributes={"class": "required"}) argument so widgets can override their attributes with those on creation. This would avoid the need of adding custom widget code for that simple task. OTOH I think the best would be not to provide the shortcut because it sort of mixes the database configuration and the client user interface. I'd instead subclass the widgets so they add the class to the html if the field is required.

Massimo Di Pierro

unread,
Aug 25, 2013, 9:59:31 PM8/25/13
to web...@googlegroups.com
I like this better than the original proposal but I am still unhappy. I think Field(...widget=...) is already too much coupling between the database layer and the presentation (form) layer. I do not think this coupling should be increased. Can we do something like:

db.table.field.widget.attributes = ... 

mr.freeze

unread,
Aug 25, 2013, 10:15:04 PM8/25/13
to web...@googlegroups.com
Precisely why I am pushing for a generic onrender method. Adding an attributes property to either the field or the widget only couples it to the presentation layer further.  Also, attributes are only one aspect of the usage. Others would be value based modification, wrapping the element or appending child elements.  What about db.table.widget.onrender = ....? The crux is that default widgets aren't applied until a form is rendered. This might need to be changed.

Massimo Di Pierro

unread,
Aug 25, 2013, 10:23:39 PM8/25/13
to web...@googlegroups.com
Please open a ticket with your patch so I do not forget. I will look at it carefully tomorrow. I welcome more discussion on the topic.

mr.freeze

unread,
Aug 26, 2013, 10:14:41 AM8/26/13
to web...@googlegroups.com

Massimo Di Pierro

unread,
Aug 26, 2013, 5:45:26 PM8/26/13
to web...@googlegroups.com
Can you please open an issue and add an example. this may be a bug.

Manuele Pesenti

unread,
Aug 27, 2013, 6:38:37 AM8/27/13
to web...@googlegroups.com
Il 25/08/13 00:37, mr.freeze ha scritto:
I want to take advantage of bootstrap's form validation classes so I need to add a class to the field.
if could be of any help that's the ugly way how I solved it locally :)

def dressMandatory(grid, table):
    """ Special representation for mandatory fields in grid """

    for fieldname in table.fields:
        myid = '_'.join((table._tablename, fieldname, ))
        # icon = I(_class="icon-warning-sign")
        app = '' # SPAN(icon, _class="add-on") if table[fieldname].required else ''
        class_value = "control-group warning" if table[fieldname].required else ''
        grid.elements('#%s' % myid, replace=lambda el: DIV(DIV(TAG.nobr(el, app), _class="input-append"), _class=class_value))

def dressMandatoryInForm(form, *tabs):
    """ Special representation for mandatory fields in form """

    tabs_fields = [[t[f] for f in t.fields if f != t._id.name] for t in tabs]
    fields_list = sum(tabs_fields[1:], tabs_fields[0])
    m = form.elements()[0].elements
    for field in fields_list:
        if field.writable and field.readable and not field.compute:
            if field.required:
                m('#no_table_%s' % field.name, replace=lambda el: DIV(DIV(el, _class="input-append"), _class="control-group warning"))

mr.freeze

unread,
Aug 27, 2013, 11:33:39 PM8/27/13
to web...@googlegroups.com
Thanks Manuele.  I am hoping that a generic onrender callback for default widgets will simplify this. 

Vinicius Assef

unread,
Aug 28, 2013, 7:05:02 AM8/28/13
to web2py
Great idea, the onrender callback.
> --
>
> ---
> 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+un...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.

mr.freeze

unread,
Sep 6, 2013, 11:45:34 AM9/6/13
to web...@googlegroups.com
Hi Massimo, have you had a chance to look at this? I am just wondering if I need to create a workaround outside of the framework or if this will be incorporated. No worries either way.
Reply all
Reply to author
Forward
0 new messages