If you benefit from web2py hope you feel encouraged to pay it forward by contributing back to society in whatever form you choose!

Leave the db.py as Mr.Freeze built it - we need this for the "things" model.

In model/A_widgets.py add the following:

def __process_data(dt,level,maxlevels,db): # this is butt-ugly ... but seems to work ! header = dt[0] data = dt[1:] if level == 1: cnt = len(db[0]) + 1 db[0].append((cnt,header,None)) else: lvl = level - 1 plvl = lvl - 1 cnt = len(db[lvl]) + 1 pcnt = len(db[plvl]) db[lvl].append((cnt,header,pcnt)) if level < maxlevels-1: for t in data: __process_data(t,level+1,maxlevels,db) else: for r in data: for t in r: lvl = maxlevels-1 plvl = lvl - 1 cnt = len(db[lvl]) + 1 pcnt = len(db[plvl]) db[lvl].append((cnt,t,pcnt))

def process_it(hd,lst): depth = len(dhead) db = [[] for d in range(depth)] # one list per heading for t in lst: __process_data(t,1,depth,db) return db

class ListCascadingSelect(object): """ Creates dependent selects based on a recursive list-of-lists. Pass the list headings in order of least to most specific. Based on http://web2pyslices.com/main/slices/take_slice/85 """ def init(self, headings, datalist): self.datalist = datalist self.tables = headings self.prompt = lambda table:str(table)
def widget(self,f,v): uid = str(uuid.uuid4())[:8] d_id = "cascade-" + uid wrapper = TABLE(id=d_id) parent = None; parent_format = None; fn = '' vr = 'var dd%s = [];var oi%s = [];\n' % (uid,uid) prompt = [self.prompt(table) for table in self.tables] vr += 'var pr%s = ["' % uid + '","'.join([str(p) for p in prompt]) + '"];\n' f_inp = SQLFORM.widgets.string.widget(f,v) f_id = f_inp['_id'] f_inp['_type'] = "hidden" datadict = process_it(self.tables,self.datalist) for tc, table in enumerate(self.tables):
db = table
format = table + "_name" options = datadict[tc] id = str(table) opts = [OPTION(opt,_value=rowid, _parent=str(parent) if parent else '0') \ for (rowid,opt,parent) in options] opts.insert(0, OPTION(prompt[tc],_value=0)) inp = SELECT(opts ,_parent=str(parent) + \ "
" + str(parent_format), _id=id,_name=id, _disabled="disabled" if parent else None) wrapper.append(TR(inp)) next = str(tc + 1) vr += 'var p%s = jQuery("#%s #%s"); dd%s.push(p%s);\n' % (tc,d_id,id,uid,tc)
vr += 'var i%s = jQuery("option",p%s).clone(); oi%s.push(i%s);\n' % (tc,tc,uid,tc) fn_in = 'for (i=%s;i<%s;i+=1){dd%s[i].find("option").remove();'\ 'dd%s[i].append(\'<option value="0">\' + pr%s[i] + \'</option>\');'\ 'dd%s[i].attr("disabled","disabled");}\n' % \ (next,len(self.tables),uid,uid,uid,uid) fn_in +='oi%s[%s].each(function(i){'\ 'if (jQuery(this).attr("parent") == dd%s[%s].val()){'\ 'dd%s[%s].append(this);}});' % (uid,next,uid,tc,uid,next)
fn_in += 'dd%s[%s].removeAttr("disabled");\n' % (uid,next) fn_in += 'jQuery("#%s").val("");' % f_id if (tc < len(self.tables)-1): fn += 'dd%s[%s].change(function(){%s});\n' % (uid,tc,fn_in) else: fn_in = 'jQuery("#%s").val(jQuery(this).val());' % f_id fn += 'dd%s[%s].change(function(){%s});\n' % (uid,tc,fn_in) if v: fn += 'dd%s[%s].val(%s);' % (uid,tc,v)
parent = table parent_format = format

    wrapper.append(f_inp)
    wrapper.append(SCRIPT(vr,fn))
    return wrapper

Then in controllers/default.py I hacked the following ...

dhead = ("State", "City", "Zipcode")

dlist = ( ("Texas", ("Austin", ("78704","78745")), ("Dallas", ("75001","75038")) ), ("Illinois", ("Chicago", ("60606","60607")), ("Aurora", ("60504","60505")) ), ("California", ("Los Angeles", ("90005","90006")), ("San Diego", ("92101","92102")) ) )

def cascading_select(): #cascade = CascadingSelect(db.state,db.city,db.zipcode) Mr.Freeze's call - cascade = ListCascadingSelect(dhead,dlist) db.things.location.widget = cascade.widget form = SQLFORM(db.things) return dict(form=form)

Ok - that's it. To be frank I hardly understood what Mr.Freeze did - while I understand Python quite well, javascripy/ajax/css/html is beyond me, so I had to build on his talented work. I make no claims on the above code - in the end it's just a hack on existing code.

Related slices

Comments (4)

  • Login to post



  • 0
    mrfreeze 14 years ago
    It would be great if you could format the code so that it's easy to copy and paste.

  • 0
    mrgrieves 14 years ago
    I pasted it in directly from my editor - it was indented at that stage. Can someone give me a pointer on exactly how to paste code into a slice AND keep the formatting .... please :-) Also I fixed a few issues which only came to light when I tried to actually retrieve the selection, so I think an improved version is in the offing, once I can get posted properly.

  • 0
    mrfreeze 14 years ago
    You just need to select the parts that are code then hit the 'Code' button on the toolbar.

  • 0
    bebers 14 years ago
    hey, any chance you could format this code? it's tough to decipher without indents/line breaks

Hosting graciously provided by:
Python Anywhere