one-to-many relations and form for crerate records

204 views
Skip to first unread message

Кирилл Шаталаев

unread,
Aug 16, 2014, 9:44:09 AM8/16/14
to web...@googlegroups.com

Hello.

This is just a simple example, my actual model is more complex, but I think I can explain the main idea.

Model:

db.define_table('users',
                Field('name', 'string'))

db.define_table('contacts',
                Field('user', db.users),
                Field('phone', 'string'))
                

Well, each user can have from 1 to infinite phone numbers.
I want user entering all his numbers while registering.

So, what are the ways to realize it?

1) Split registration process to steps, step one - enter name, step two - add phones one by one. Ugly.
2) Use smartgrid. Have "parent-children" out of the box. Being, may be, cool tool for admin purposes, it is absolutely unintuitive and unusable for end-users.
3) Make custom form with jquery: form have button "add another phone number", user press the button and another text field adding dynamicaly for the "infinite" form. Excellent way. But. This is theoretically. And I cant find any practical examples.

So, testing I can see that inputs with same name processing by web2py as a list, its fine! For example, there is a duplicated inputs in the form:

<input name="phone" type="text">
<input name="phone" type="text">

after submitting, I have ['123456','345678'] on form.vars.phone. Cool. Cycle and do "db.insert()" voila, you say!

No.

Linked table "contacts" actually is more more complex. It have "address" string, "state" string, "postal", validators and another linked tables such "postals", "cities", etc, etc...

And I have no idea how build my form and how to process my form vars within controller with all "contact" table validators?
Is there any elegant way doing this?

Found this:
not so helpful.

Richard Vézina

unread,
Aug 18, 2014, 2:27:32 PM8/18/14
to web2py-users
# Controller

def phone_numbers_form():
    user_id = None
    if request.args(0) is not None:
        try:
            user_id = int(request.args(0))
        except ValueError:
            user_id = db(db.auth_user.username == request.args(0)).select(db.auth_user.id).first().id
    if user_id is None:
        user_id = auth.user_id
    if request.vars.remove_phone_number_id is not None:
        db(db.phone_number.id == int(request.vars.remove_phone_number_id)).delete()
    record_to_modify = request.vars.request_modify_phone_id
    phone_numbers = db(db.phone_number.user_id == user_id).select()
    db.phone_number.user_id.default = user_id
    db.phone_number.user_id.update = user_id
    if record_to_modify is None:
        buttons = [
            TAG.button(I('', _class='icon-ban-circle icon-white'), _type='button',
                       _onClick='web2py_component("%s","phone_number_component");' % URL(c='directory',
                                                                                         f='phone_numbers_form',
                                                                                         extension='load',
                                                                                         args=request.args(0)),
                       _class='btn btn-mini btn-inverse'),
            TAG.button((I('', _class='icon-ok icon-white'), CAT('  '), STRONG(T('Add'))), _type='submit',
                       _class='btn btn-mini btn-primary')]
    else:
        buttons = [
            TAG.button(I('', _class='icon-ban-circle icon-white'), _type='button',
                       _onClick='web2py_component("%s","phone_number_component");' % URL(c='directory',
                                                                                         f='phone_numbers_form',
                                                                                         extension='load',
                                                                                         args=request.args(0)),
                       _class='btn btn-mini btn-inverse'),
            TAG.button((I('', _class='icon-ok icon-white'), CAT('  '), T('Ok')), _type='submit',
                       _class='btn btn-mini btn-primary')]
        # form.element(_type='submit')['_value'] = T('ok')
        # form.element(_type='submit')['_class'] = 'btn btn-mini pull-right'
    form = SQLFORM(db.phone_number, record=None if record_to_modify is None else int(record_to_modify), buttons=buttons)
    if form.process().accepted:
        if record_to_modify is not None:
            response.flash = T('Successfully inserted')
        else:
            response.flash = T('Successfully updated')
        response.js = 'web2py_component("%(URL)s","phone_number_component");' % {'URL': URL(c='directory',
                                                                                            f='phone_numbers_form',
                                                                                            extension='load',
                                                                                            args=request.args(0))}
    elif form.errors:
        response.flash = T('Errors...')
    return dict(phone_numbers=phone_numbers, form=form)


# View - COMPONENT

<strong>{{=T('Phone numbers')}}</strong>
{{=form.custom.begin}}
<table class="table table-condensed table-hover">
    <tbody>
        {{for phone_number in phone_numbers:}}
            <tr>
                {{if request.vars.request_modify_phone_id is not None and phone_number.id == int(request.vars.request_modify_phone_id):}}
                    <td>{{=form.custom.widget.phone_number_kind_id}}</td>
                    <td>{{=form.custom.widget.phone_number}}</td>
                    <td>
                        <span style="display: none;">{{=form.custom.widget.user_id}}</span>
                        {{=form.custom.widget.extension}}
                        <div class="pull-right">
                        {{=A(I('', _class='icon-ban-circle icon-white'), _class='btn btn-mini btn-inverse',
                             _href=URL('phone_numbers_form', extension='load', args=request.args(0)), cid=request.cid)}}
                        {{=BUTTON(I('', _class='icon-ok icon-white'), _class='btn btn-mini btn-primary', _type='submit')}}
                        </div>
                    </td>
                {{else:}}
                    <td>{{=db.phone_number_kind[phone_number.phone_number_kind_id].phone_number_kind}}</td>
                    <td>{{=phone_number.phone_number}}</td>
                    <td>
                        {{if phone_number.extension is not None and phone_number.extension != '':}}
                            ({{=phone_number.extension}})
                        {{pass}}
                        <div class="pull-right">
                        {{=A(I('',_class='icon-edit'), _class='btn btn-mini',
                             _href=URL('phone_numbers_form', extension='load', args=request.args(0),
                                       vars=dict(request_modify_phone_id=phone_number.id)), cid=request.cid)}}
                        {{=A(I('',_class='icon-remove icon-white'), _class='btn btn-mini btn-danger',
                             _href=URL('phone_numbers_form', extension='load', args=request.args(0),
                                       vars=dict(remove_phone_number_id=phone_number.id)), cid=request.cid)}}
                        </div>
                    </td>
                {{pass}}
            </tr>
        {{pass}}
        {{if request.vars.request_modify_phone_id == None:}}
            <tr id="input_new_phone_number">
                <td>{{=form.custom.widget.phone_number_kind_id}}</td>
                <td>{{=form.custom.widget.phone_number}}</td>
                <td>{{=form.custom.widget.extension}}
                    <span style="display: none;">{{=form.custom.widget.user_id}}</span>
                    <span class='pull-right'>{{=form.custom.submit}}</span>
                </td>
            </tr>
        {{pass}}
    </tbody>
</table>
{{=form.custom.end}}

<script>
$(document).ready(function() {
    var add_phone_number_translation = '{{=T('Add a phone number')}}'
    $("tr#input_new_phone_number").children().hide();
    $("tr#input_new_phone_number").append('<td colspan="3" id="new_phone_number"><button type="button" class="btn btn-small pull-right" onclick="$(\'tr#input_new_phone_number\').children().show(); $(\'td#new_phone_number\').hide();"><strong>' + add_phone_number_translation + '</strong></button></td>');
    });
</script>


The view is a component so you have a form in the main view for your contact and this component for adding phone numbers.

Richard


--
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+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Alex

unread,
Aug 20, 2014, 3:39:18 PM8/20/14
to web...@googlegroups.com

For dynamic pages I'd recommend to use a javascript framework. When the user submits the form you make an ajax call to your 'save' controller where you verify the data (this can be done with form.accepts) and return possible errors. I'm rewriting many pages with knockout ( http://knockoutjs.com/ ) and it has really paid off for me.

There are also lots of other JS frameworks around, here is a good overview:

http://todomvc.com/


Alex

Cliff Kachinske

unread,
Aug 20, 2014, 10:28:41 PM8/20/14
to web...@googlegroups.com
Here is what I would do.

On the client have a var contact_count = 0

All of your starting contact fields will have a suffix "__0"

When the user clicks the add contact button, use Javascript to increment the contact_count and write another set of contact fields. This time set the suffix to "__1"

Increment the number in the suffix accordingly each time the user clicks the add contact button.

Use form submission as usual.

In the controller you can link the sets of contact fields together by the suffix number.
Reply all
Reply to author
Forward
0 new messages