Search code examples
controllerweb2py

web2py record id not being passed to onvalidation function for SQLFORM


I am trying to use the same controller function for insert and update by calling different SQLFORMs depending on existence of arguments passed to the function:

def manage_doc():
    db.doc_master.sr_no.readable=False
    db.doc_master.sr_no.writable=False

    if not len(request.args):
        form = SQLFORM(db.doc_master)
    else:
        doc_to_edit = db.doc_master(request.args[0])
        form = SQLFORM(db.doc_master, doc_to_edit)

    if form.process(onvalidation=check_form_manage_doc).accepted:
        if not len(request.args):
            session.flash = 'Document added'
        else:
            session.flash = 'Document updated'
        redirect(URL('index'))

    return dict(form=form)

This is the onvalidation callback:

def check_form_manage_doc(form):
    new_inward_no = form.vars.inward_no
    new_inward_year = form.vars.inward_date.year
    if (form.vars.doc_date > form.vars.inward_date):
        form.errors.doc_date = 'Document Date cannot be later than Inward Date'
    rows = db(db.doc_master.inward_date.year() == new_inward_year)
    for row in rows.select():
        if ((row.id != form.vars.id) and (row.inward_no == new_inward_no)):
            form.errors.inward_no = 'Inward No %s already exists for Doc %s' % (str(new_inward_no), str(form.vars.id))
    form.vars.sr_no = str(new_inward_no)+'/'+str(new_inward_year)

While the function is working OK on insertion, on update it fails because form.vars.id is always None in the callback function. What is wrong with my code?


Solution

  • form.vars.id is not populated at the time that onvalidation is run. You can instead use request.post_vars.id, which will of course continue to be None for record creation but will contain the record ID for updates (request.post_vars.id will be a string, so convert to int when not None).

    As an aside, note there is no reason to conditionally create two different forms -- you can instead simply do:

    form = SQLFORM(db.doc_master, record=request.args(0))
    

    request.args(0) will simply be None if there are no URL args. Also, note that the record argument can simply be the record ID rather than a DAL Row object containing the record.

    Also, note that the .process method includes some arguments that can further simplify your code -- all of your form code can be replaced with:

    form = SQLFORM(db.doc_master, request.args(0)).process(
        onvalidation=check_form_manage_doc,
        message_onsuccess='Document %s' % ('updated' if request.args else 'added'),
        next=URL('index'))