how do I create, multiple forms and precess them, using 'for' statement?

97 views
Skip to first unread message

Ivo

unread,
Dec 17, 2013, 6:14:25 AM12/17/13
to web...@googlegroups.com
I want to create multiple forms with a 'for' statement and validate/process them.
the amount of forms to generate is dependent on a list containing user id's.
the list is dynamic; so I have a function generating a list, 'users', which contains: user:1L, user:4L, etc...
now I want to create a form for each of them and after 'submit' process them.

Normally I would just create a form in the controller like:
def show_form():
   form = SQLFORM.x()
   return dict(form=form)
and process it with:
   if form.accepts

however this would only generate one form... 
using:
def sow_form():
    for user in users:
        form = SQLFORM.x()
   return dict(form=form) 
and calling it from the view with {{=form}} doesn't work either because of 'form' being static.

Creating the form from the view is a lot easier to do with:
{{for user in users:}}
(my form)
{{pass}}

but how do I process it?
I can give the form a unique name from the view with name="{{=user}}"
but then what?

The form is mostly prepopulated with vars created/calculated from the view but a part of those can be created/calculated from the controller. the form requires a uuid which needed in the processing of the form.

What is the best way to achieve this?
would:
def show_form():
    for user in users:
        form[user] = SQLFORM.x()
work? and how can I populate that form?

Please point me in the right direction.


Niphlod

unread,
Dec 17, 2013, 2:54:33 PM12/17/13
to web...@googlegroups.com
you need to use different formname(s) to be able to process them separately.
take a look here
http://web2py.com/books/default/chapter/29/07/forms-and-validators#Multiple-forms-per-page

Massimo Di Pierro

unread,
Dec 17, 2013, 3:03:50 PM12/17/13
to web...@googlegroups.com
What does the model look like? You amy want to create a single form with fields for each user.

Ivo

unread,
Dec 17, 2013, 5:12:36 PM12/17/13
to web...@googlegroups.com
I understand that I need to use different formnames, I just don't understand how I would syntax it do that automatically.
A single form will not suffice; I think is has to be different forms. I need a uuid for each user and create/update several records for each user.
Basically I need to use something like:

def show_form():
    for user in users:
        form= SQLFORM.factory()
    return dict(form=form)
But then in a way that works. I tried several ways, but am still unsuccessful...
I read (, re-read and re-re-read) the chapter on forms in 'the book' but there's no mention on how to achieve something like this, neither in the examples.
I tried digging through a lot of the sample apps too but nothing.

I understand this is not the basic way forms are designed to be used but is it that uncommon to do this?
I also tried:
for user in users:
    form[user]= SQLFORM.factory()
return dict(form[user]=form[user])
this throws a global name form is not defined.

I also tried:
for user in users:
    form = SQLFORM.factory()
    form[user] = form

any ideas?

Niphlod

unread,
Dec 17, 2013, 5:21:32 PM12/17/13
to web...@googlegroups.com
you just need to figure out a "key" to separate different forms, and use formname accordingly as stated in the book.
I do it in w2p_tvseries already, you can take a look at
https://github.com/niphlod/w2p_tvseries/blob/master/controllers/manage.py
specifically, see lines #169, #239 and #266

ivo.ni...@gmail.com

unread,
Dec 17, 2013, 5:27:18 PM12/17/13
to web...@googlegroups.com
Thank you! Will spend tomorrow figuring it out. Thanks for the example!

Sent from my iPhone
--
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 a topic in the Google Groups "web2py-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/web2py/7O1sR3M4BRw/unsubscribe.
To unsubscribe from this group and all its topics, send an email to web2py+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Ivo

unread,
Dec 18, 2013, 9:16:06 AM12/18/13
to
Hi Niphlod,
I studied your example, but can't quite get it to work yet. I also tried taking a look at your view but it's to complicated for me.
The only issue I'm having now is that the key isn't incrementing from the view side. Can I pass the key in the view like {{=form+key}} or something? 
Maybe I'm screwing up my controller.

controller:
def show_form():
    
    users_1 = db(db.people.id.belongs(session.people.keys())).select(db.people.user_id)
    users_2=[]
    for user in users_1:
        if user not in users_2:
            users_2.append(user)
        else:
            pass
    
   
    for user in users_2:
        usr= db(db.auth_user.id==user.user_id).select(db.auth_user.username,db.auth_user.id)[0]
        usr1=usr.username
        usr2=usr.id
        key=str(usr2)
        fields = [
                  Field('username',default=usr1,writable=False)
                  ]
        form=SQLFORM.factory(*fields, table_name=key,hidden=dict(ss_id=key))
    
    return dict(form=form, users_2=users_2,usr1=usr1,usr2=usr2,key=key)


View:
{{extend 'layout.html'}}
<h1>Form:</h1>
{{for user in users_2:}}

{{=form}}
{{pass}}

<!-- returns a form for each user but uses the same key for every form; result is that the default value is the same for every form-->

<h1>
    custom form:
</h1>
{{=form.custom.begin}}

{{for user in users_2:}}

User: {{=form.custom.widget.username}}<br>

{{pass}}
{{form.custom.end}}
<!-- returns a form for each user but uses the same key for every form; result is that the default value is the same for every form-->

<h1>
    manual form: gives the correct results
</h1>
{{for user in users_2:}}
{{
        usr= db(db.auth_user.id==user.user_id).select(db.auth_user.username,db.auth_user.id)[0]
        usr1=usr.username
        usr2=usr.id
        key=str(usr2)
}}
KEY:{{=key}}<br> <!-- here to check that the key changes correctly -->
<form action="#" enctype="multipart/form-data" method="post"><table><tr id="{{=key}}_user__row"><td class="w2p_fl"><label for="{{=key}}_user" id="{{=key}}_user__label">username: </label></td><td class="w2p_fw">{{=usr1}}</td><td class="w2p_fc"></td></tr><tr id="submit_record__row"><td class="w2p_fl"></td><td class="w2p_fw"><input type="submit" value="Submit" /></td><td class="w2p_fc"></td></tr></table><div style="display:none;"><input name="ss_id" type="hidden" value="{{=key}}" /></div></form>
{{pass}}



I prefer to use the {{form.custom.widget[Field]}} tags.
Can you tell me what I'm doing wrong?

Niphlod

unread,
Dec 18, 2013, 2:41:11 PM12/18/13
to web...@googlegroups.com
you are returning only the last form....... you need to make a dict or a list out of the single form you generate in the for loop, and then in the view you can "unpack" them separately

in my code, you can see on #L296 how every single form is appended to "forms", that is a list and that list is returned to the view.

Also, you're leaving out  form.accepts (or form.process )... you can't access form data in your controller without it

Ivo

unread,
Dec 18, 2013, 3:17:27 PM12/18/13
to web...@googlegroups.com
Thanks, I understand what you mean.
I left the form.accepts out as it wasn't relevant to my question (and am still designing it) but thanks for pointing it out anyway!

I'm going to struggle with the unpacking part for a while and  thanks for not spoonfeeding me the code! The goal is me actually learning something ;) after a few months I'm finally at a point where I can grasp the concepts and code somewhat.

Niphlod

unread,
Dec 18, 2013, 3:45:14 PM12/18/13
to web...@googlegroups.com
form.process adds little bits to the form to be able to actually process the posted data.

in pseudo-code you need to

list_of_forms = []
for something in list_of_values:
     key = something_unique
     form = SQLFORM(....)
     if form.process(formname=something_unique).accepted:
           .....code dealing with the posted values, such as form.vars, etc
     elif form.errors:
           .....code dealing with form errors

     list_of_forms.append(form)
    ......
    return dict(list_of_forms=list_of_forms, ....)

In the view, you can then:

{{for single_form in list_of_forms:}}
<div class="row">
{{=single_form}} #or {{single_form.custom.etc etc etc}}
</div>
<hr />
{{pass}}

Ivo

unread,
Dec 20, 2013, 9:16:34 AM12/20/13
to web...@googlegroups.com
I have chosen to go the custom form way.
The only issue I have is that upon hitting submit, the button submits all the forms on the page instead of just the one owning the button. I think this also screws up my validation and it prevents records from being inserted to the db.  

controller:
def show_form():
    
    users_1 = db(db.people.id.belongs(session.people.keys())).select(db.people.user_id)
    users_2=[]
    for user in users_1:
        if user not in users_2:
            users_2.append(user)
        else:
            pass

    forms=[]  
    for user in users_2:
        usr= db(db.auth_user.id==user.user_id).select(db.auth_user.username,db.auth_user.id)[0]
        usr1=usr.username
        usr2=usr.id
        key=str(usr2)
        fields = [
                  Field('key',default=key,writable=False),
                  Field('username',default=usr1,writable=False),
                  Field('address',default='Fill in your address',type='text')
                  ]

        form=SQLFORM.factory(*fields, table_name=key,hidden=dict(ss_id=key),
                                             buttons=[
                                                  BUTTON("Save Changes", _class="btn btn-primary")])
        if form.process(formname=key, hideerror=True).accepted:
            db.parking.insert(name=usr1, address=form.vars.address)
        forms.append(form)
    
    return dict(forms=forms)



view:
{{for form in forms:}}
{{=form.custom.begin}}
User: {{=form.custom.widget.username}} <br>
Address: {{=form.custom.widget.address}} <br>
{{form.custom.end}}<br><br>
{{pass}}

Ivo

unread,
Dec 21, 2013, 5:27:13 AM12/21/13
to web...@googlegroups.com
I got it resolved... ashamed to say it but I missed the = in the {{form.custom.end}} tag :(

Niphlod

unread,
Dec 21, 2013, 7:47:42 AM12/21/13
to web...@googlegroups.com
don't worry, it's a mistake you'll probably never do again :P
Reply all
Reply to author
Forward
0 new messages