Okay, here's the core of the code. First, this is the function that creates 
the form (in a component and submitted via ajax). It's a generic form for 
editing db records based on a url arg supplying the table name. You'll 
notice that I have to use a hack at the moment to get the values from my 
custom form (AjaxSelect) to submit. The widget code is below:


    def editform(self, rargs=None, rvars=None):
        """
        """
        db = current.db

        flash = ''
        rjs = ''
        duplink = ''
        default_vars = {}

        if rargs is not None:
            tablename = rargs[0]
            showid = rvars['showid'] or True
            dbio = False if 'dbio' in rvars.keys() and rvars['dbio'] == 
'False' else True
            formstyle = rvars['formstyle'] or 'ul'
            deletable = rvars['deletable'] or True
            copylabel = rvars['copylabel'] or SPAN(_class='glyphicon 
glyphicon-file')
            orderby = rvars['orderby'] or 'id'
            restrictor = rvars['restrictor'] or None
            collation = rvars['collation'] or None
            postprocess = rvars['postprocess'] or None

            if len(rargs) > 1:  # editing specific item
                rowid = rargs[1]
                formname = '{}/{}'.format(tablename, rowid)
                formargs = [db[tablename], rowid]

                # create a link for adding a new row to the table
                duplink = A(copylabel,
                            _href=URL('plugin_listandedit',
                                      'dupAndEdit.load',
                                      args=[tablename, rowid],
                                      vars=rvars),
                            _class='plugin_listandedit_duplicate',
                            cid='viewpane')

            elif len(rargs) == 1:  # creating new item
                formname = '{}/create'.format(tablename)
                default_vars = {k: v for k, v in rvars.iteritems()
                                if hasattr(db[tablename], k)}
                formargs = [db[tablename]]

            form = self._myform(formargs,
                                deletable=deletable,
                                showid=showid,
                                formstyle=formstyle)
            # print {'default_vars': default_vars}
            # for k in default_vars: form.vars.setitem(k, default_vars[k])
            for k in default_vars: form.vars[k] = default_vars[k]

            # FIXME: ajaxselect field values have to be added manually
            # FIXME: this check will fail if ajaxselect widget is for field 
indx[1]
            if db[tablename].fields[1] in rvars.keys():
                extras = [f for f in db[tablename].fields
                          if f not in form.vars.keys()]
                for e in extras:
                    form.vars[e] = rvars[e] if e in rvars.keys() \
                        else ''
                if 'id' in form.vars.keys() and form.vars['id'] in (None, ''
):
                    del(form.vars['id'])
            else:
                pass
            # print 'form vars in editform 
---------------------------------'
            # pprint(form.vars)

            if form.process(formname=formname, dbio=dbio).accepted:
                flash = ''
                if postprocess:
                    flash += '{} '.format(self._post_process(form.vars, 
postprocess))
                if dbio:
                    flash += 'The changes were recorded successfully.'

                # either redirect or refresh the list pane
                if 'redirect' in rvars and 'True' == rvars['redirect']:
                    redirect(URL(rvars['redirect_c'], rvars['redirect_a']))
                else:
                    the_url = URL('plugin_listandedit', 'itemlist.load',
                                  args=[tablename], vars={'orderby': orderby
,
                                                          'restrictor': 
restrictor,
                                                          'collation': 
collation})
                    rjs = "window.setTimeout(web2py_component('{}', " \
                          "'listpane'), 500);".format(the_url)
            elif form.errors:
                print '\n\nlistandedit form errors:'
                pprint({k: v for k, v in form.errors.iteritems()})
                print '\n\nlistandedit form vars'
                pprint({k: v for k, v in form.vars.iteritems()})
                print '\n\nlistandedit request vars'
                pprint({k: v for k, v in rvars.iteritems()})
                flash = 'Sorry, there was an error processing ' \
                                'the form. The changes have not been 
recorded.'

            else:
                pass

        else:
            flash = 'Sorry, you need to specify a type of record before' \
                             'I can list the records.'
            form = None

        return form, duplink, flash, rjs


Now here's the first bit of the custom widget class. The thing to 
understand here is that the widget is refreshable (via ajax) without 
affecting the rest of the form. So you can update the select options via 
ajax from the database while you're in the midst of filling in the form. I 
do this by creating a web2py component to hold the form. So the 
AjaxSelect.widget() method below sets up the component, and the 
set_widget() controller function calls back to AjaxSelect.widget_content() 
to actually create the widget instance. (The widget also does other things 
like display a sortable list of selected options from a multi-select and 
dynamically add new db entries to the linked table.) If you want to see the 
full plugin code for the widget it's on github here: 
https://github.com/monotasker/plugin_ajaxselect

I'm wondering whether putting the widget in a component is what creates the 
problem. If so I'd really like to find a way around that. It's a very 
powerful and configurable widget that (if I can get the last kinks worked 
out) I'd like to share with the community.

class AjaxSelect(object):
    """
    """

    def __init__(self, field, value, indx=0,
                 refresher=None, adder=True,
                 restricted=None, restrictor=None,
                 multi=True, lister=False,
                 rval=None, sortable=False,
                 orderby=None):

        # raw args
        self.field = field
        self.indx = indx
        self.refresher = refresher
        self.adder = adder
        # isolate setting of param for easy overriding in subclasses
        self.restricted = self.restrict(restricted)
        self.restrictor = restrictor
        self.multi = multi
        self.lister = lister
        self.rval = rval
        self.sortable = sortable
        self.orderby = orderby

        # find table referenced by widget
        self.fieldset = str(field).split('.')
        self.linktable = get_linktable(field)
        # processed variables
        self.wrappername = self.get_wrappername(self.fieldset)
        self.form_name = '%s_adder_form' % self.linktable  # for referenced 
table form

        # get the field value (choosing db or session here)
        self.value = self.choose_val(value)
        try:
            if value and len(value) > 0:
                self.clean_val = ','.join(map(str, value))
            else:
                self.clean_val = value
        except TypeError:
            self.clean_val = value
        # args for add and refresh urls
        self.uargs = self.fieldset
        # vars for add and refresh urls
        self.uvars = {'wrappername': self.wrappername,
                      'refresher': refresher,
                      'adder': self.adder,
                      'restrictor': self.restrictor,
                      'multi': self.multi,
                      'lister': self.lister,
                      'restricted': self.restricted,
                      'sortable': self.sortable,
                      'orderby': self.orderby,
                      'indx': self.indx}

    def widget(self):
        """
        Place initial load container for controller to fill.
        """
        # prepare classes for widget wrapper
        wclasses = self.get_classes(self.linktable, self.restricted,
                                    self.restrictor, self.lister, self.
sortable)
        uvars = self.uvars
        uvars.update({self.fieldset[1]: self.value})
        # create SPAN to wrap widget
        wrapper = SPAN(_id=self.wrappername, _class=wclasses)
        wrapper.append(LOAD('plugin_ajaxselect', 'set_widget.load',
                            args=self.uargs, vars=uvars,
                            target=self.wrappername,
                            ajax=False))
        return wrapper


    def widget_contents(self):
        """
        Main method to create the ajaxselect widget. Calls helper methods
        and returns the wrapper element containing all associated elements
        """
        #session = current.session
        #request = current.request

        wrapper = CAT()

        # create and add content of SPAN
        widget = self.create_widget()
        refreshlink = self.make_refresher(self.wrappername, self.linktable,
                                          self.uargs, self.uvars)
        adder, modal = self.make_adder(self.wrappername, self.linktable)
        wrapper.components.extend([widget, refreshlink, adder])

        # create and add tags/links if multiple select widget
        if self.multi and (self.lister == 'simple'):
            taglist = self.make_taglist()
        elif self.multi and (self.lister == 'editlinks'):
            taglist = self.make_linklist()
        else:
            taglist = ''
        wrapper.append(taglist)

        return wrapper, modal

    def create_widget(self):
        """
        create either a single select widget or multiselect widget
        """
        if not self.multi in [None, False, 'False']:
            if self.orderby:
                w = FilteredMultipleOptionsWidget.widget(self.field, self.
value,
                                                 orderby=self.orderby,
                                                 multiple='multiple')
            else:
                w = MultipleOptionsWidget.widget(self.field, self.value)
            #place selected items at end of sortable select widget
            if self.sortable:
                try:
                    for v in self.value:
                        opt = w.element(_value=v)
                        i = w.elements().index(opt)
                        w.append(opt)
                        del w[i - 1]
                except AttributeError, e:
                    if type(v) == 'IntType':
                        opt = w.element(_value=self.value)
                        i = w.elements().index(opt)
                        w.append(opt)
                        del w[i - 1]
                    else:
                        print e
                except Exception, e:
                        print e, type(e)
        else:
            if self.orderby:
                w = FilteredOptionsWidget.widget(self.field, self.value,
                                                 orderby=self.orderby)
            else:
                w = OptionsWidget.widget(self.field, self.value)

        w['_id'] = '{}_{}'.format(self.fieldset[0], self.fieldset[1])
        w['_name'] = self.fieldset[1]
        return w








On Thursday, February 18, 2016 at 8:33:23 PM UTC-5, Anthony wrote:
>
> When you pass a record to SQLFORM, it retains the changed values by 
> passing the values in request.post_vars to the respective field widgets. 
> Can't say what's going on in your case without the code.
>
> Anthony
>
> On Thursday, February 18, 2016 at 4:13:23 PM UTC-5, Ian W. Scott wrote:
>>
>> The code is very extensive (it's a complex widget) so first I'm just 
>> trying to understand what happens in a normal form. You say that other 
>> widgets aren't preserving values from the db. So where do the values come 
>> from if you have keepvalues=True?
>>
>> Also, this *is* a situation where an existing record is loaded into the 
>> form, then modified on form submission. So how does the form present to me 
>> the values I just changed if it's not coming from the db? I think if I can 
>> understand how that works I'll be able to solve the problem in my code 
>> myself.
>>
>> Thanks,
>>
>> Ian
>>
>> On Thursday, February 18, 2016 at 4:06:06 PM UTC-5, Anthony wrote:
>>>
>>> On Thursday, February 18, 2016 at 3:54:43 PM UTC-5, Ian W. Scott wrote:
>>>>
>>>> Okay, that explains the behaviour. How, then, are values usually 
>>>> preserved on an ajax form for the built-in widgets? When I submit the form 
>>>> all of the other fields (not using my widget) preserve the newly submitted 
>>>> values.
>>>>
>>>
>>> Again, hard to say what's going on in your case without seeing any code, 
>>> but the other widgets are not presenting values from the database (unless a 
>>> pre-existing record has been loaded).
>>>
>>> Anthony
>>>
>>

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

Reply via email to