def index(): """ example action using the internationalization operator T and flash rendered by views/default/index.html or views/generic.html if you need a simple wiki simple replace the two lines below with: return auth.wiki() """ row=db(content.html).select().last() form=FORM('html',TEXTAREA(row.html,_id="textarea_id",_name="content" ,_style="width:700px;height:300px;"), INPUT(_type='submit'), next=URL('default','index')) if form.process().accepted: session.flash = form.vars content.insert(html= form.vars['content']) elif form.errors: response.flash = 'form has errors' else: response.flash = 'please fill the form' return dict(form=form,req=form.vars)
def accepts( self, request_vars, session=None, formname='%(tablename)s_%(record_id)s', keepvalues=False, onvalidation=None, dbio=True, hideerror=False, ): """ same as FORM.accepts but also does insert, update or delete in SQLDB. """ keyed = hasattr(self.table, '_primarykey') if self.record: if keyed: formname_id = '.'.join([ str(self.record[k]) for k in self.table._primarykey if hasattr(self.record, k) ]) record_id = dict([(k, request_vars[k]) for k in self.table._primarykey]) else: (formname_id, record_id) = \ (self.record.id, request_vars.get('id', None)) keepvalues = True else: if keyed: formname_id = 'create' record_id = dict([(k, None) for k in self.table._primarykey]) else: (formname_id, record_id) = ('create', None) if not keyed and isinstance(record_id, (list, tuple)): record_id = record_id[0] if formname: formname = formname % dict(tablename=self.table._tablename, record_id=formname_id) # ## THIS IS FOR UNIQUE RECORDS, read IS_NOT_IN_DB for fieldname in self.fields: field = self.table[fieldname] requires = field.requires or [] if not isinstance(requires, (list, tuple)): requires = [requires] [ item.set_self_id(self.record_id) for item in requires if hasattr(item, 'set_self_id') and self.record_id ] # ## END fields = {} for key in self.vars: fields[key] = self.vars[key] ret = FORM.accepts( self, request_vars, session, formname, keepvalues, onvalidation, hideerror=hideerror, ) if not ret and self.record and self.errors: ### if there are errors in update mode # and some errors refers to an already uploaded file # delete error if # - user not trying to upload a new file # - there is existing file and user is not trying to delete it # this is because removing the file may not pass validation for key in self.errors.keys(): if self.table[key].type == 'upload' \ and request_vars.get(key,None) in (None,'') \ and self.record[key] \ and not key+UploadWidget.ID_DELETE_SUFFIX in request_vars: del self.errors[key] if not self.errors: ret = True requested_delete = \ request_vars.get(self.FIELDNAME_REQUEST_DELETE, False) self.custom.end = TAG[''](self.hidden_fields(), self.custom.end) auch = record_id and self.errors and requested_delete # auch is true when user tries to delete a record # that does not pass validation, yet it should be deleted if not ret and not auch: for fieldname in self.fields: field = self.table[fieldname] if fieldname in self.vars: value = self.vars[fieldname] elif self.record: value = self.record[fieldname] else: value = self.table[fieldname].default #was value = request_vars[fieldname] if hasattr(field, 'widget') and field.widget\ and fieldname in request_vars: row_id = '%s_%s%s' % (self.table, fieldname, SQLFORM.ID_ROW_SUFFIX) widget = field.widget(field, value) self.field_parent[row_id].components = [widget] self.field_parent[row_id]._traverse(False, hideerror) self.custom.widget[fieldname] = widget return ret if record_id and str(record_id) != str(self.record_id): raise SyntaxError, 'user is tampering with form\'s record_id: ' \ '%s != %s' % (record_id, self.record_id) if requested_delete and self.custom.deletable: if dbio: if keyed: qry = reduce(lambda x, y: x & y, [ self.table[k] == record_id[k] for k in self.table._primarykey ]) if self.table._db(qry).delete(): self.vars.update(record_id) else: self.table._db(self.table.id == self.record.id).delete() self.vars.id = self.record.id self.errors.clear() for component in self.elements('input, select, textarea'): component['_disabled'] = True return True for fieldname in self.fields: if not fieldname in self.table: continue if not self.ignore_rw and not self.table[fieldname].writable: continue field = self.table[fieldname] if field.type == 'id': continue if field.type == 'boolean': if self.vars.get(fieldname, False): self.vars[fieldname] = fields[fieldname] = True else: self.vars[fieldname] = fields[fieldname] = False elif field.type == 'password' and self.record\ and request_vars.get(fieldname, None) == \ PasswordWidget.DEFAULT_PASSWORD_DISPLAY: continue # do not update if password was not changed elif field.type == 'upload': f = self.vars[fieldname] fd = fieldname + '__delete' if f == '' or f == None: if self.vars.get(fd, False) or not self.record: fields[fieldname] = '' else: fields[fieldname] = self.record[fieldname] self.vars[fieldname] = fields[fieldname] continue elif hasattr(f, 'file'): (source_file, original_filename) = (f.file, f.filename) elif isinstance(f, (str, unicode)): ### do not know why this happens, it should not (source_file, original_filename) = \ (cStringIO.StringIO(f), 'file.txt') newfilename = field.store(source_file, original_filename) # this line is for backward compatibility only self.vars['%s_newfilename' % fieldname] = newfilename fields[fieldname] = newfilename if isinstance(field.uploadfield, str): fields[field.uploadfield] = source_file.read() # proposed by Hamdy (accept?) do we need fields at this point? self.vars[fieldname] = fields[fieldname] continue elif fieldname in self.vars: fields[fieldname] = self.vars[fieldname] elif field.default == None and field.type != 'blob': self.errors[fieldname] = 'no data' return False if field.type == 'integer': if fields[fieldname] != None: fields[fieldname] = int(fields[fieldname]) elif str(field.type).startswith('reference'): if fields[fieldname] != None and isinstance( self.table, Table) and not keyed: fields[fieldname] = int(fields[fieldname]) elif field.type == 'double': if fields[fieldname] != None: fields[fieldname] = float(fields[fieldname]) for fieldname in self.vars: if fieldname != 'id' and fieldname in self.table.fields\ and not fieldname in fields and not fieldname\ in request_vars: fields[fieldname] = self.vars[fieldname] if dbio: if keyed: if reduce(lambda x, y: x and y, record_id.values()): # if record_id if fields: qry = reduce(lambda x, y: x & y, [ self.table[k] == self.record[k] for k in self.table._primarykey ]) self.table._db(qry).update(**fields) else: pk = self.table.insert(**fields) if pk: self.vars.update(pk) else: ret = False else: if record_id: self.vars.id = self.record.id if fields: self.table._db(self.table.id == self.record.id).update( **fields) else: self.vars.id = self.table.insert(**fields) return ret
def __init__(self, table, record=None, deletable=False, linkto=None, upload=None, fields=None, labels=None, col3={}, submit_button='Submit', delete_label='Check to delete:', showid=True, readonly=False, comments=True, keepopts=[], ignore_rw=False, record_id=None, formstyle='table3cols', **attributes): """ SQLFORM(db.table, record=None, fields=['name'], labels={'name': 'Your name'}, linkto=URL(r=request, f='table/db/') """ self.ignore_rw = ignore_rw self.formstyle = formstyle nbsp = XML(' ') # Firefox2 does not display fields with blanks FORM.__init__(self, *[], **attributes) ofields = fields keyed = hasattr(table, '_primarykey') # if no fields are provided, build it from the provided table # will only use writable or readable fields, unless forced to ignore if fields == None: fields = [ f.name for f in table if (ignore_rw or f.writable or f.readable) and not f.compute ] self.fields = fields # make sure we have an id if self.fields[0] != table.fields[0] and \ isinstance(table,Table) and not keyed: self.fields.insert(0, table.fields[0]) self.table = table # try to retrieve the indicated record using its id # otherwise ignore it if record and isinstance(record, (int, long, str, unicode)): if not str(record).isdigit(): raise HTTP(404, "Object not found") record = table._db(table.id == record).select().first() if not record: raise HTTP(404, "Object not found") self.record = record self.record_id = record_id if keyed: if record: self.record_id = dict([(k, record[k]) for k in table._primarykey]) else: self.record_id = dict([(k, None) for k in table._primarykey]) self.field_parent = {} xfields = [] self.fields = fields self.custom = Storage() self.custom.dspval = Storage() self.custom.inpval = Storage() self.custom.label = Storage() self.custom.comment = Storage() self.custom.widget = Storage() self.custom.linkto = Storage() for fieldname in self.fields: if fieldname.find('.') >= 0: continue field = self.table[fieldname] comment = None if comments: comment = col3.get(fieldname, field.comment) if comment == None: comment = '' self.custom.comment[fieldname] = comment if labels != None and fieldname in labels: label = labels[fieldname] colon = '' else: label = field.label colon = ': ' self.custom.label[fieldname] = label field_id = '%s_%s' % (table._tablename, fieldname) label = LABEL(label, colon, _for=field_id, _id=field_id + SQLFORM.ID_LABEL_SUFFIX) row_id = field_id + SQLFORM.ID_ROW_SUFFIX if field.type == 'id': self.custom.dspval.id = nbsp self.custom.inpval.id = '' widget = '' if record: if showid and 'id' in fields and field.readable: v = record['id'] widget = SPAN(v, _id=field_id) self.custom.dspval.id = str(v) xfields.append((row_id, label, widget, comment)) self.record_id = str(record['id']) self.custom.widget.id = widget continue if readonly and not ignore_rw and not field.readable: continue if record: default = record[fieldname] else: default = field.default cond = readonly or \ (not ignore_rw and not field.writable and field.readable) if default and not cond: default = field.formatter(default) dspval = default inpval = default if cond: # ## if field.represent is available else # ## ignore blob and preview uploaded images # ## format everything else if field.represent: inp = field.represent(default) elif field.type in ['blob']: continue elif field.type == 'upload': inp = UploadWidget.represent(field, default, upload) else: inp = field.formatter(default) elif hasattr(field, 'widget') and field.widget: inp = field.widget(field, default) elif field.type == 'upload': inp = self.widgets.upload.widget(field, default, upload) elif field.type == 'boolean': inp = self.widgets.boolean.widget(field, default) if default: inpval = 'checked' else: inpval = '' elif OptionsWidget.has_options(field): if not field.requires.multiple: inp = self.widgets.options.widget(field, default) else: inp = self.widgets.multiple.widget(field, default) if fieldname in keepopts: inpval = TAG[''](*inp.components) elif str(field.type).startswith('list'): inp = self.widgets.list.widget(field, default) elif field.type == 'text': inp = self.widgets.text.widget(field, default) elif field.type == 'password': inp = self.widgets.password.widget(field, default) if self.record: dspval = PasswordWidget.DEFAULT_PASSWORD_DISPLAY else: dspval = '' elif field.type == 'blob': continue else: inp = self.widgets.string.widget(field, default) xfields.append((row_id, label, inp, comment)) self.custom.dspval[fieldname] = dspval or nbsp self.custom.inpval[fieldname] = inpval or '' self.custom.widget[fieldname] = inp # if a record is provided and found, as is linkto # build a link if record and linkto: for (rtable, rfield) in table._referenced_by: if keyed: rfld = table._db[rtable][rfield] query = urllib.quote( str(rfld == record[rfld.type[10:].split('.')[1]])) else: # <block> query = urllib.quote( str(table._db[rtable][rfield] == record.id)) lname = olname = '%s.%s' % (rtable, rfield) if ofields and not olname in ofields: continue if labels and lname in labels: lname = labels[lname] widget = A(lname, _class='reference', _href='%s/%s?query=%s' % (linkto, rtable, query)) xfields.append( (olname.replace('.', '__') + SQLFORM.ID_ROW_SUFFIX, '', widget, col3.get(olname, ''))) self.custom.linkto[olname.replace('.', '__')] = widget # </block> # when deletable, add delete? checkbox self.custom.deletable = '' if record and deletable: widget = INPUT( _type='checkbox', _class='delete', _id=self.FIELDKEY_DELETE_RECORD, _name=self.FIELDNAME_REQUEST_DELETE, ) xfields.append( (self.FIELDKEY_DELETE_RECORD + SQLFORM.ID_ROW_SUFFIX, LABEL(delete_label, _for=self.FIELDKEY_DELETE_RECORD, _id=self.FIELDKEY_DELETE_RECORD + SQLFORM.ID_LABEL_SUFFIX), widget, col3.get(self.FIELDKEY_DELETE_RECORD, ''))) self.custom.deletable = widget # when writable, add submit button self.custom.submit = '' if not readonly: widget = INPUT(_type='submit', _value=submit_button) xfields.append( ('submit_record' + SQLFORM.ID_ROW_SUFFIX, '', widget, col3.get('submit_button', ''))) self.custom.submit = widget # if a record is provided and found # make sure it's id is stored in the form if record: if not self['hidden']: self['hidden'] = {} if not keyed: self['hidden']['id'] = record['id'] (begin, end) = self._xml() self.custom.begin = XML("<%s %s>" % (self.tag, begin)) self.custom.end = XML("%s</%s>" % (end, self.tag)) if formstyle == 'table3cols': table = TABLE() for id, a, b, c in xfields: td_b = self.field_parent[id] = TD(b, _class='w2p_fw') table.append( TR(TD(a, _class='w2p_fl'), td_b, TD(c, _class='w2p_fc'), _id=id)) elif formstyle == 'table2cols': table = TABLE() for id, a, b, c in xfields: td_b = self.field_parent[id] = TD(b, _class='w2p_fw', _colspan="2") table.append( TR(TD(a, _class='w2p_fl'), TD(c, _class='w2p_fc'), _id=id + '1', _class='even')) table.append(TR(td_b, _id=id + '2', _class='odd')) elif formstyle == 'divs': table = TAG['']() for id, a, b, c in xfields: div_b = self.field_parent[id] = DIV(b, _class='w2p_fw') table.append( DIV(DIV(a, _class='w2p_fl'), div_b, DIV(c, _class='w2p_fc'), _id=id)) elif formstyle == 'ul': table = UL() for id, a, b, c in xfields: div_b = self.field_parent[id] = DIV(b, _class='w2p_fw') table.append( LI(DIV(a, _class='w2p_fl'), div_b, DIV(c, _class='w2p_fc'), _id=id)) elif type(formstyle) == type(lambda: None): table = TABLE() for id, a, b, c in xfields: td_b = self.field_parent[id] = TD(b, _class='w2p_fw') newrows = formstyle(id, a, td_b, c) if type(newrows).__name__ != "tuple": newrows = [newrows] for newrow in newrows: table.append(newrow) else: raise RuntimeError, 'formsyle not supported' self.components = [table]
def accepts( self, request_vars, formname='%(tablename)s_%(record_id)s', keepvalues=False, onvalidation=None, hideerror=False, ): """ similar FORM.accepts but also does insert, update or delete in SQLDB. but if detect_record_change == True than: form.record_changed = False (record is properly validated/submitted) form.record_changed = True (record cannot be submitted because changed) elseif detect_record_change == False than: form.record_changed = None """ # implement logic to detect whether record exist but has been modified # server side _vars_ = {} request_vars = copy.deepcopy(request_vars) for itm in request_vars: if isinstance(request_vars[itm], (list, tuple)): if not (str(itm) in self.table.fields and str(self.table[itm].type).startswith("list::")): request_vars[itm] = request_vars[itm][-1] if self.record: (formname_id, record_id) = (self.record.get(self.record_pk_name, None), request_vars.get(self.record_pk_name, None)) keepvalues = True else: (formname_id, record_id) = ('create', None) if formname: formname = formname % dict(tablename=self.table._tablename, record_id=formname_id) for fieldname in self.fields: field = self.table[fieldname] requires = field.requires or [] if not isinstance(requires, (list, tuple)): requires = [requires] # ## END fields = {} for key in self.vars: fields[key] = self.vars[key] ret = FORM.accepts( self, request_vars, formname, keepvalues, onvalidation, hideerror=hideerror, ) if not ret and self.record and self.errors: ### if there are errors in update mode # and some errors refers to an already uploaded file # delete error if # - user not trying to upload a new file # - there is existing file and user is not trying to delete it # this is because removing the file may not pass validation for key in self.errors.keys(): if self.table[key].type == 'upload' \ and request_vars.get(key,None) in (None,'') \ and self.record[key] \ and not key+UploadWidget.ID_DELETE_SUFFIX in request_vars: del self.errors[key] if not self.errors: ret = True requested_delete = \ request_vars.get(self.FIELDNAME_REQUEST_DELETE, False) self.custom.end = TAG[''](self.hidden_fields(), self.custom.end) auch = record_id and self.errors and requested_delete # auch is true when user tries to delete a record # that does not pass validation, yet it should be deleted if not ret and not auch: for fieldname in self.fields: field = self.table[fieldname] ### this is a workaround! widgets should always have default not None! if not field.widget and field.type.startswith('list:') and \ not OptionsWidget.has_options(field): field.widget = self.widgets.list.widget if hasattr(field, 'widget' ) and field.widget and fieldname in request_vars: if fieldname in self.vars: value = self.vars[fieldname] elif self.record: value = self.record[fieldname] else: value = self.table[fieldname].default row_id = '%s_%s%s' % (self.table, fieldname, FORMBUILDER.ID_ROW_SUFFIX) widget = field.widget(field, value) self.field_parent[row_id].components = [widget] if not field.type.startswith('list:'): self.field_parent[row_id]._traverse(False, hideerror) self.custom.widget[fieldname] = widget return ret self.record_id = record_id if requested_delete and self.custom.deletable: self.errors.clear() for component in self.elements('input, select, textarea'): component['_disabled'] = True return True for fieldname in self.fields: if not fieldname in self.table: continue if not self.ignore_rw and not self.table[fieldname].writable: ### this happens because FROM has no knowledge of writable ### and thinks that a missing boolean field is a None if self.table[fieldname].type == 'boolean' and self.vars[ fieldname] == None: del self.vars[fieldname] continue field = self.table[fieldname] if field.type == 'id': continue if field.type == 'boolean': if self.vars.get(fieldname, False): self.vars[fieldname] = fields[fieldname] = True else: self.vars[fieldname] = fields[fieldname] = False elif field.type == 'password' and self.record\ and request_vars.get(fieldname, None) == \ PasswordWidget.DEFAULT_PASSWORD_DISPLAY: continue # do not update if password was not changed elif field.type == 'upload': if self.vars.get("%s__delete" % fieldname, False): self.vars[fieldname] = "" del self.vars["%s__delete" % fieldname] continue if self.custom_file: self.vars[fieldname] = self.custom_file( field, request_vars) else: self.vars[fieldname] = field.store( request_vars.get(fieldname, ""), request_vars.get("%s.original" % fieldname, "")) continue elif fieldname in self.vars: fields[fieldname] = self.vars[fieldname] elif field.default == None and field.type != 'blob': self.errors[fieldname] = 'no data' return False value = fields.get(fieldname, None) if field.type == 'list:string': if not isinstance(value, (tuple, list)): fields[fieldname] = value and [value] or [] elif field.type.startswith('list:'): if not isinstance(value, list): fields[fieldname] = [ safe_int(x) for x in (value and [value] or []) ] elif field.type == 'integer': if value != None: fields[fieldname] = safe_int(value) elif field.type == 'double': if value != None: fields[fieldname] = safe_float(value) for fieldname in self.vars: if fieldname != 'id' and fieldname in self.table.fields\ and not fieldname in fields and not fieldname\ in request_vars: fields[fieldname] = self.vars[fieldname] return ret
def __init__(self, table, record=None, deletable=False, download="", upload=None, fields=None, labels=None, col3={}, submit_button='Submit', delete_label='Check to delete:', showid=True, readonly=False, comments=True, keepopts=[], ignore_rw=False, formstyle='divs', record_pk_name='_id', tabs=[], **attributes): """ FORMBUILDER(db.table, record=None, fields=['name'], labels={'name': 'Your name'}, """ self.table = table if record: for itm in record: if isinstance(record[itm], (list, tuple)): if not (itm in self.table.fields and self.table[itm].type.startswith("list::")): record[itm] = record[itm][-1] self.custom_file = upload self.ignore_rw = ignore_rw self.formstyle = formstyle self.record_pk_name = record_pk_name nbsp = XML(' ') # Firefox2 does not display fields with blanks attributes.update({"_id": "hyform_%s" % self.table._tablename}) FORM.__init__(self, *[], **attributes) ofields = fields # if no fields are provided, build it from the provided table # will only use writable or readable fields, unless forced to ignore if fields == None: fields = [ f.name for f in table if (ignore_rw or f.writable or f.readable) ] self.record = record self.field_parent = {} xfields = {} xfields_keys = [] self.fields = fields self.custom = Storage() self.custom.dspval = Storage() self.custom.inpval = Storage() self.custom.label = Storage() self.custom.comment = Storage() self.custom.widget = Storage() for fieldname in self.fields: if fieldname.find('.') >= 0: continue field = self.table[fieldname] comment = None if comments: comment = col3.get(fieldname, field.comment) if comment == None: comment = '' self.custom.comment[fieldname] = comment if labels != None and fieldname in labels: label = labels[fieldname] colon = '' else: label = field.label colon = ': ' self.custom.label[fieldname] = label field_id = '%s_%s' % (table._tablename, fieldname) label = LABEL(label, colon, _for=field_id, _id=field_id + FORMBUILDER.ID_LABEL_SUFFIX) row_id = field_id + FORMBUILDER.ID_ROW_SUFFIX if readonly and not ignore_rw and not field.readable: continue if record: default = record.get(fieldname, field.default) else: default = field.default cond = readonly or \ (not ignore_rw and not field.writable and field.readable) if default and not cond: default = field.formatter(default) dspval = default inpval = default if cond: # ## if field.represent is available else # ## ignore blob and preview uploaded images # ## format everything else if field.represent: inp = field.represent(default) elif field.type in ['blob']: continue elif field.type == 'upload': inp = UploadWidget.represent(field, default, download) elif field.type == 'boolean': inp = self.widgets.boolean.widget(field, default, _disabled=True) else: inp = field.formatter(default) elif field.type == 'upload': if hasattr(field, 'widget') and field.widget: inp = field.widget(field, default, download) else: inp = self.widgets.upload.widget(field, default, download) elif hasattr(field, 'widget') and field.widget: inp = field.widget(field, default) elif field.type == 'boolean': inp = self.widgets.boolean.widget(field, default) if default: inpval = 'checked' else: inpval = '' elif OptionsWidget.has_options(field): if not field.requires.multiple: inp = self.widgets.options.widget(field, default) else: inp = self.widgets.multiple.widget(field, default) if fieldname in keepopts: inpval = TAG[''](*inp.components) elif field.type.startswith('list:'): inp = self.widgets.list.widget(field, default) elif field.type == 'text': inp = self.widgets.text.widget(field, default) elif field.type == 'hidden': inp = self.widgets.hidden.widget(field, default) elif field.type == 'password': inp = self.widgets.password.widget(field, default) if self.record: dspval = PasswordWidget.DEFAULT_PASSWORD_DISPLAY else: dspval = '' elif field.type == 'blob': continue else: inp = self.widgets.string.widget(field, default) xfields_keys.append(fieldname) xfields[fieldname] = (row_id, label, inp, comment) self.custom.dspval[fieldname] = dspval or nbsp self.custom.inpval[fieldname] = inpval or '' self.custom.widget[fieldname] = inp # when writable, add submit button self.custom.submit = '' if not readonly: widget = INPUT(_type='submit', _class="submit", _value=submit_button, _id="submit_%s" % self.table._tablename) self.custom.submit = widget # if a record is provided and found # make sure it's id is stored in the form if record: if not self['hidden']: self['hidden'] = {} (begin, end) = self._xml() self.custom.begin = XML("<%s %s>" % (self.tag, begin)) self.custom.end = XML("%s</%s>" % (end, self.tag)) table = TAG['']() if formstyle == 'divs': # tabs = [ # {"tabname":"personal","caption":"个人信息","fields":["login","age"]}, # {"tabname":"comp","caption":"公司信息","fields":["login","age"]}, # {"tabname":"gf","caption":"朋友信息","fields":["login","age"]}, # ] if tabs: tab_headers = UL() tab_bodys = [] for i, tab in enumerate(tabs): tbody = DIV(_id="body_%s" % (tab.get("tabname", ""))) if i == 0: li = LI(tab.get("caption", ""), _class="active", _id="tab_%s" % (tab.get("tabname", ""))) tbody['_class'] = "active" else: li = LI(tab.get("caption", ""), _style="display:none;", _id="tab_%s" % (tab.get("tabname", ""))) tbody['_style'] = "display:none;" tab_headers.append(li) for f in tab.get("fields", []): if f in xfields_keys: id, a, b, c = xfields[f] div_b = self.field_parent[id] = DIV( b, _class='w2p_fw') tbody.append( DIV(DIV(a, _class='w2p_fl'), div_b, DIV(c, _class='w2p_fc'), _id=id, _class="fb_row")) tab_bodys.append(tbody) tab_headers = DIV(tab_headers, _id="tab_headers_%s" % self.table._tablename) tab_bodys = DIV(tab_bodys, _id="tab_bodys_%s" % self.table._tablename) table.append(DIV(tab_headers, tab_bodys)) else: for d in xfields_keys: id, a, b, c = xfields[d] div_b = self.field_parent[id] = DIV(b, _class='w2p_fw') table.append( DIV(DIV(a, _class='w2p_fl'), div_b, DIV(c, _class='w2p_fc'), _id=id, _class="fb_row")) self.components = [table, self.custom.submit]
def accepts( self, request_vars, formname='%(tablename)s_%(record_id)s', keepvalues=False, onvalidation=None, hideerror=False, ): """ similar FORM.accepts but also does insert, update or delete in SQLDB. but if detect_record_change == True than: form.record_changed = False (record is properly validated/submitted) form.record_changed = True (record cannot be submitted because changed) elseif detect_record_change == False than: form.record_changed = None """ # implement logic to detect whether record exist but has been modified # server side _vars_ = {} request_vars = copy.deepcopy(request_vars) for itm in request_vars: if isinstance(request_vars[itm],(list,tuple)): if not (str(itm) in self.table.fields and str(self.table[itm].type).startswith("list::")): request_vars[itm] = request_vars[itm][-1] if self.record: (formname_id, record_id) = ( self.record.get(self.record_pk_name, None), request_vars.get(self.record_pk_name, None)) keepvalues = True else: (formname_id, record_id) = ('create', None) if formname: formname = formname % dict(tablename = self.table._tablename, record_id = formname_id) for fieldname in self.fields: field = self.table[fieldname] requires = field.requires or [] if not isinstance(requires, (list, tuple)): requires = [requires] # ## END fields = {} for key in self.vars: fields[key] = self.vars[key] ret = FORM.accepts( self, request_vars, formname, keepvalues, onvalidation, hideerror=hideerror, ) if not ret and self.record and self.errors: ### if there are errors in update mode # and some errors refers to an already uploaded file # delete error if # - user not trying to upload a new file # - there is existing file and user is not trying to delete it # this is because removing the file may not pass validation for key in self.errors.keys(): if self.table[key].type == 'upload' \ and request_vars.get(key,None) in (None,'') \ and self.record[key] \ and not key+UploadWidget.ID_DELETE_SUFFIX in request_vars: del self.errors[key] if not self.errors: ret = True requested_delete = \ request_vars.get(self.FIELDNAME_REQUEST_DELETE, False) self.custom.end = TAG[''](self.hidden_fields(), self.custom.end) auch = record_id and self.errors and requested_delete # auch is true when user tries to delete a record # that does not pass validation, yet it should be deleted if not ret and not auch: for fieldname in self.fields: field = self.table[fieldname] ### this is a workaround! widgets should always have default not None! if not field.widget and field.type.startswith('list:') and \ not OptionsWidget.has_options(field): field.widget = self.widgets.list.widget if hasattr(field, 'widget') and field.widget and fieldname in request_vars: if fieldname in self.vars: value = self.vars[fieldname] elif self.record: value = self.record[fieldname] else: value = self.table[fieldname].default row_id = '%s_%s%s' % (self.table,fieldname,FORMBUILDER.ID_ROW_SUFFIX) widget = field.widget(field, value) self.field_parent[row_id].components = [ widget ] if not field.type.startswith('list:'): self.field_parent[row_id]._traverse(False,hideerror) self.custom.widget[ fieldname ] = widget return ret self.record_id = record_id if requested_delete and self.custom.deletable: self.errors.clear() for component in self.elements('input, select, textarea'): component['_disabled'] = True return True for fieldname in self.fields: if not fieldname in self.table: continue if not self.ignore_rw and not self.table[fieldname].writable: ### this happens because FROM has no knowledge of writable ### and thinks that a missing boolean field is a None if self.table[fieldname].type == 'boolean' and self.vars[fieldname]==None: del self.vars[fieldname] continue field = self.table[fieldname] if field.type == 'id': continue if field.type == 'boolean': if self.vars.get(fieldname, False): self.vars[fieldname] = fields[fieldname] = True else: self.vars[fieldname] = fields[fieldname] = False elif field.type == 'password' and self.record\ and request_vars.get(fieldname, None) == \ PasswordWidget.DEFAULT_PASSWORD_DISPLAY: continue # do not update if password was not changed elif field.type == 'upload': if self.vars.get("%s__delete"%fieldname,False): self.vars[fieldname] = "" del self.vars["%s__delete"%fieldname] continue if self.custom_file: self.vars[fieldname] = self.custom_file(field, request_vars) else: self.vars[fieldname] = field.store(request_vars.get(fieldname,""),request_vars.get("%s.original"%fieldname,"")) continue elif fieldname in self.vars: fields[fieldname] = self.vars[fieldname] elif field.default == None and field.type!='blob': self.errors[fieldname] = 'no data' return False value = fields.get(fieldname,None) if field.type == 'list:string': if not isinstance(value,(tuple,list)): fields[fieldname] = value and [value] or [] elif field.type.startswith('list:'): if not isinstance(value,list): fields[fieldname] = [safe_int(x) for x in (value and [value] or [])] elif field.type == 'integer': if value != None: fields[fieldname] = safe_int(value) elif field.type == 'double': if value != None: fields[fieldname] = safe_float(value) for fieldname in self.vars: if fieldname != 'id' and fieldname in self.table.fields\ and not fieldname in fields and not fieldname\ in request_vars: fields[fieldname] = self.vars[fieldname] return ret
def __init__( self, table, record = None, deletable = False, download = "", upload = None, fields = None, labels = None, col3 = {}, submit_button = 'Submit', delete_label = 'Check to delete:', showid = True, readonly = False, comments = True, keepopts = [], ignore_rw = False, formstyle = 'divs', record_pk_name = '_id', tabs = [], **attributes ): """ FORMBUILDER(db.table, record=None, fields=['name'], labels={'name': 'Your name'}, """ self.table = table if record: for itm in record: if isinstance(record[itm],(list,tuple)): if not (itm in self.table.fields and self.table[itm].type.startswith("list::")): record[itm] = record[itm][-1] self.custom_file = upload self.ignore_rw = ignore_rw self.formstyle = formstyle self.record_pk_name = record_pk_name nbsp = XML(' ') # Firefox2 does not display fields with blanks attributes.update({"_id":"hyform_%s"%self.table._tablename}) FORM.__init__(self, *[], **attributes) ofields = fields # if no fields are provided, build it from the provided table # will only use writable or readable fields, unless forced to ignore if fields == None: fields = [f.name for f in table if (ignore_rw or f.writable or f.readable)] self.record = record self.field_parent = {} xfields = {} xfields_keys = [] self.fields = fields self.custom = Storage() self.custom.dspval = Storage() self.custom.inpval = Storage() self.custom.label = Storage() self.custom.comment = Storage() self.custom.widget = Storage() for fieldname in self.fields: if fieldname.find('.') >= 0: continue field = self.table[fieldname] comment = None if comments: comment = col3.get(fieldname, field.comment) if comment == None: comment = '' self.custom.comment[fieldname] = comment if labels != None and fieldname in labels: label = labels[fieldname] colon = '' else: label = field.label colon = ': ' self.custom.label[fieldname] = label field_id = '%s_%s' % (table._tablename, fieldname) label = LABEL(label, colon, _for=field_id, _id=field_id+FORMBUILDER.ID_LABEL_SUFFIX) row_id = field_id + FORMBUILDER.ID_ROW_SUFFIX if readonly and not ignore_rw and not field.readable: continue if record: default = record.get(fieldname, field.default) else: default = field.default cond = readonly or \ (not ignore_rw and not field.writable and field.readable) if default and not cond: default = field.formatter(default) dspval = default inpval = default if cond: # ## if field.represent is available else # ## ignore blob and preview uploaded images # ## format everything else if field.represent: inp = field.represent(default) elif field.type in ['blob']: continue elif field.type == 'upload': inp = UploadWidget.represent(field, default, download) elif field.type == 'boolean': inp = self.widgets.boolean.widget(field, default, _disabled=True) else: inp = field.formatter(default) elif field.type == 'upload': if hasattr(field, 'widget') and field.widget: inp = field.widget(field, default, download) else: inp = self.widgets.upload.widget(field, default, download) elif hasattr(field, 'widget') and field.widget: inp = field.widget(field, default) elif field.type == 'boolean': inp = self.widgets.boolean.widget(field, default) if default: inpval = 'checked' else: inpval = '' elif OptionsWidget.has_options(field): if not field.requires.multiple: inp = self.widgets.options.widget(field, default) else: inp = self.widgets.multiple.widget(field, default) if fieldname in keepopts: inpval = TAG[''](*inp.components) elif field.type.startswith('list:'): inp = self.widgets.list.widget(field,default) elif field.type == 'text': inp = self.widgets.text.widget(field, default) elif field.type == 'hidden': inp = self.widgets.hidden.widget(field, default) elif field.type == 'password': inp = self.widgets.password.widget(field, default) if self.record: dspval = PasswordWidget.DEFAULT_PASSWORD_DISPLAY else: dspval = '' elif field.type == 'blob': continue else: inp = self.widgets.string.widget(field, default) xfields_keys.append(fieldname) xfields[fieldname] = (row_id,label,inp,comment) self.custom.dspval[fieldname] = dspval or nbsp self.custom.inpval[fieldname] = inpval or '' self.custom.widget[fieldname] = inp # when writable, add submit button self.custom.submit = '' if not readonly: widget = INPUT(_type='submit',_class="submit", _value=submit_button, _id = "submit_%s"%self.table._tablename ) self.custom.submit = widget # if a record is provided and found # make sure it's id is stored in the form if record: if not self['hidden']: self['hidden'] = {} (begin, end) = self._xml() self.custom.begin = XML("<%s %s>" % (self.tag, begin)) self.custom.end = XML("%s</%s>" % (end, self.tag)) table = TAG['']() if formstyle == 'divs': # tabs = [ # {"tabname":"personal","caption":"个人信息","fields":["login","age"]}, # {"tabname":"comp","caption":"公司信息","fields":["login","age"]}, # {"tabname":"gf","caption":"朋友信息","fields":["login","age"]}, # ] if tabs: tab_headers = UL() tab_bodys = [] for i, tab in enumerate(tabs): tbody = DIV(_id="body_%s"%(tab.get("tabname",""))) if i == 0: li = LI(tab.get("caption",""),_class="active", _id="tab_%s"%(tab.get("tabname",""))) tbody['_class'] = "active" else: li = LI(tab.get("caption",""),_style="display:none;",_id="tab_%s"%(tab.get("tabname",""))) tbody['_style'] = "display:none;" tab_headers.append(li) for f in tab.get("fields",[]): if f in xfields_keys: id,a,b,c = xfields[f] div_b = self.field_parent[id] = DIV(b,_class='w2p_fw') tbody.append(DIV(DIV(a,_class='w2p_fl'),div_b,DIV(c,_class='w2p_fc'),_id=id, _class="fb_row")) tab_bodys.append(tbody) tab_headers = DIV(tab_headers, _id="tab_headers_%s"%self.table._tablename) tab_bodys = DIV(tab_bodys, _id="tab_bodys_%s"%self.table._tablename) table.append(DIV(tab_headers, tab_bodys)) else: for d in xfields_keys: id,a,b,c = xfields[d] div_b = self.field_parent[id] = DIV(b,_class='w2p_fw') table.append(DIV(DIV(a,_class='w2p_fl'), div_b, DIV(c,_class='w2p_fc'),_id=id,_class="fb_row")) self.components = [table, self.custom.submit]
def accepts( self, request_vars, session=None, formname='%(tablename)s_%(record_id)s', keepvalues=False, onvalidation=None, dbio=True, hideerror=False, detect_record_change=False, ): """ similar FORM.accepts but also does insert, update or delete in SQLDB. but if detect_record_change == True than: form.record_changed = False (record is properly validated/submitted) form.record_changed = True (record cannot be submitted because changed) elseif detect_record_change == False than: form.record_changed = None """ if request_vars.__class__.__name__ == 'Request': request_vars = request_vars.post_vars keyed = hasattr(self.table,'_primarykey') # implement logic to detect whether record exist but has been modified # server side self.record_changed = None if detect_record_change: if self.record: self.record_changed = False serialized = '|'.join(str(self.record[k]) for k in self.table.fields()) self.record_hash = md5_hash(serialized) # logic to deal with record_id for keyed tables if self.record: if keyed: formname_id = '.'.join(str(self.record[k]) for k in self.table._primarykey if hasattr(self.record,k)) record_id = dict((k,request_vars[k]) for k in self.table._primarykey) else: (formname_id, record_id) = (self.record.id, request_vars.get('id', None)) keepvalues = True else: if keyed: formname_id = 'create' record_id = dict([(k,None) for k in self.table._primarykey]) else: (formname_id, record_id) = ('create', None) if not keyed and isinstance(record_id, (list, tuple)): record_id = record_id[0] if formname: formname = formname % dict(tablename = self.table._tablename, record_id = formname_id) # ## THIS IS FOR UNIQUE RECORDS, read IS_NOT_IN_DB for fieldname in self.fields: field = self.table[fieldname] requires = field.requires or [] if not isinstance(requires, (list, tuple)): requires = [requires] [item.set_self_id(self.record_id) for item in requires if hasattr(item, 'set_self_id') and self.record_id] # ## END fields = {} for key in self.vars: fields[key] = self.vars[key] ret = FORM.accepts( self, request_vars, session, formname, keepvalues, onvalidation, hideerror=hideerror, ) if not ret and self.record and self.errors: ### if there are errors in update mode # and some errors refers to an already uploaded file # delete error if # - user not trying to upload a new file # - there is existing file and user is not trying to delete it # this is because removing the file may not pass validation for key in self.errors.keys(): if self.table[key].type == 'upload' \ and request_vars.get(key,None) in (None,'') \ and self.record[key] \ and not key+UploadWidget.ID_DELETE_SUFFIX in request_vars: del self.errors[key] if not self.errors: ret = True requested_delete = \ request_vars.get(self.FIELDNAME_REQUEST_DELETE, False) self.custom.end = TAG[''](self.hidden_fields(), self.custom.end) auch = record_id and self.errors and requested_delete # auch is true when user tries to delete a record # that does not pass validation, yet it should be deleted if not ret and not auch: for fieldname in self.fields: field = self.table[fieldname] ### this is a workaround! widgets should always have default not None! if not field.widget and field.type.startswith('list:') and \ not OptionsWidget.has_options(field): field.widget = self.widgets.list.widget if hasattr(field, 'widget') and field.widget and fieldname in request_vars: if fieldname in self.vars: value = self.vars[fieldname] elif self.record: value = self.record[fieldname] else: value = self.table[fieldname].default row_id = '%s_%s%s' % (self.table,fieldname,SQLFORM.ID_ROW_SUFFIX) widget = field.widget(field, value) self.field_parent[row_id].components = [ widget ] if not field.type.startswith('list:'): self.field_parent[row_id]._traverse(False,hideerror) self.custom.widget[ fieldname ] = widget return ret if record_id and str(record_id) != str(self.record_id): raise SyntaxError, 'user is tampering with form\'s record_id: ' \ '%s != %s' % (record_id, self.record_id) if requested_delete and self.custom.deletable: if dbio: if keyed: qry = reduce(lambda x,y: x & y, [self.table[k]==record_id[k] for k in self.table._primarykey]) if self.table._db(qry).delete(): self.vars.update(record_id) else: self.table._db(self.table.id == self.record.id).delete() self.vars.id = self.record.id self.errors.clear() for component in self.elements('input, select, textarea'): component['_disabled'] = True return True for fieldname in self.fields: if not fieldname in self.table: continue if not self.ignore_rw and not self.table[fieldname].writable: continue field = self.table[fieldname] if field.type == 'id': continue if field.type == 'boolean': if self.vars.get(fieldname, False): self.vars[fieldname] = fields[fieldname] = True else: self.vars[fieldname] = fields[fieldname] = False elif field.type == 'password' and self.record\ and request_vars.get(fieldname, None) == \ PasswordWidget.DEFAULT_PASSWORD_DISPLAY: continue # do not update if password was not changed elif field.type == 'upload': f = self.vars[fieldname] fd = fieldname + '__delete' if f == '' or f == None: if self.vars.get(fd, False) or not self.record: fields[fieldname] = '' else: fields[fieldname] = self.record[fieldname] self.vars[fieldname] = fields[fieldname] continue elif hasattr(f,'file'): (source_file, original_filename) = (f.file, f.filename) elif isinstance(f, (str, unicode)): ### do not know why this happens, it should not (source_file, original_filename) = \ (cStringIO.StringIO(f), 'file.txt') newfilename = field.store(source_file, original_filename) # this line is for backward compatibility only self.vars['%s_newfilename' % fieldname] = newfilename fields[fieldname] = newfilename if isinstance(field.uploadfield,str): fields[field.uploadfield] = source_file.read() # proposed by Hamdy (accept?) do we need fields at this point? self.vars[fieldname] = fields[fieldname] continue elif fieldname in self.vars: fields[fieldname] = self.vars[fieldname] elif field.default == None and field.type!='blob': self.errors[fieldname] = 'no data' return False value = fields.get(fieldname,None) if field.type == 'list:string': if not isinstance(value,(tuple,list)): fields[fieldname] = value and [value] or [] elif field.type.startswith('list:'): if not isinstance(value,list): fields[fieldname] = [safe_int(x) for x in (value and [value] or [])] elif field.type == 'integer': if value != None: fields[fieldname] = safe_int(value) elif field.type.startswith('reference'): if value != None and isinstance(self.table,Table) and not keyed: fields[fieldname] = safe_int(value) elif field.type == 'double': if value != None: fields[fieldname] = safe_float(value) for fieldname in self.vars: if fieldname != 'id' and fieldname in self.table.fields\ and not fieldname in fields and not fieldname\ in request_vars: fields[fieldname] = self.vars[fieldname] if dbio: if keyed: if reduce(lambda x,y: x and y, record_id.values()): # if record_id if fields: qry = reduce(lambda x,y: x & y, [self.table[k]==self.record[k] for k in self.table._primarykey]) self.table._db(qry).update(**fields) else: pk = self.table.insert(**fields) if pk: self.vars.update(pk) else: ret = False else: if record_id: self.vars.id = self.record.id if fields: self.table._db(self.table.id == self.record.id).update(**fields) else: self.vars.id = self.table.insert(**fields) return ret
def __init__( self, table, record = None, deletable = False, linkto = None, upload = None, fields = None, labels = None, col3 = {}, submit_button = 'Submit', delete_label = 'Check to delete:', showid = True, readonly = False, comments = True, keepopts = [], ignore_rw = False, record_id = None, formstyle = 'table3cols', **attributes ): """ SQLFORM(db.table, record=None, fields=['name'], labels={'name': 'Your name'}, linkto=URL(r=request, f='table/db/') """ self.ignore_rw = ignore_rw self.formstyle = formstyle nbsp = XML(' ') # Firefox2 does not display fields with blanks FORM.__init__(self, *[], **attributes) ofields = fields keyed = hasattr(table,'_primarykey') # if no fields are provided, build it from the provided table # will only use writable or readable fields, unless forced to ignore if fields == None: fields = [f.name for f in table if (ignore_rw or f.writable or f.readable) and not f.compute] self.fields = fields # make sure we have an id if self.fields[0] != table.fields[0] and \ isinstance(table,Table) and not keyed: self.fields.insert(0, table.fields[0]) self.table = table # try to retrieve the indicated record using its id # otherwise ignore it if record and isinstance(record, (int, long, str, unicode)): if not str(record).isdigit(): raise HTTP(404, "Object not found") record = table._db(table.id == record).select().first() if not record: raise HTTP(404, "Object not found") self.record = record self.record_id = record_id if keyed: if record: self.record_id = dict([(k,record[k]) for k in table._primarykey]) else: self.record_id = dict([(k,None) for k in table._primarykey]) self.field_parent = {} xfields = [] self.fields = fields self.custom = Storage() self.custom.dspval = Storage() self.custom.inpval = Storage() self.custom.label = Storage() self.custom.comment = Storage() self.custom.widget = Storage() self.custom.linkto = Storage() for fieldname in self.fields: if fieldname.find('.') >= 0: continue field = self.table[fieldname] comment = None if comments: comment = col3.get(fieldname, field.comment) if comment == None: comment = '' self.custom.comment[fieldname] = comment if labels != None and fieldname in labels: label = labels[fieldname] colon = '' else: label = field.label colon = ': ' self.custom.label[fieldname] = label field_id = '%s_%s' % (table._tablename, fieldname) label = LABEL(label, colon, _for=field_id, _id=field_id+SQLFORM.ID_LABEL_SUFFIX) row_id = field_id+SQLFORM.ID_ROW_SUFFIX if field.type == 'id': self.custom.dspval.id = nbsp self.custom.inpval.id = '' widget = '' if record: if showid and 'id' in fields and field.readable: v = record['id'] widget = SPAN(v, _id=field_id) self.custom.dspval.id = str(v) xfields.append((row_id,label, widget,comment)) self.record_id = str(record['id']) self.custom.widget.id = widget continue if readonly and not ignore_rw and not field.readable: continue if record: default = record[fieldname] else: default = field.default cond = readonly or \ (not ignore_rw and not field.writable and field.readable) if default and not cond: default = field.formatter(default) dspval = default inpval = default if cond: # ## if field.represent is available else # ## ignore blob and preview uploaded images # ## format everything else if field.represent: inp = field.represent(default) elif field.type in ['blob']: continue elif field.type == 'upload': inp = UploadWidget.represent(field, default, upload) else: inp = field.formatter(default) elif hasattr(field, 'widget') and field.widget: inp = field.widget(field, default) elif field.type == 'upload': inp = self.widgets.upload.widget(field, default, upload) elif field.type == 'boolean': inp = self.widgets.boolean.widget(field, default) if default: inpval = 'checked' else: inpval = '' elif OptionsWidget.has_options(field): if not field.requires.multiple: inp = self.widgets.options.widget(field, default) else: inp = self.widgets.multiple.widget(field, default) if fieldname in keepopts: inpval = TAG[''](*inp.components) elif field.type.startswith('list:'): inp = self.widgets.list.widget(field,default) elif field.type == 'text': inp = self.widgets.text.widget(field, default) elif field.type == 'password': inp = self.widgets.password.widget(field, default) if self.record: dspval = PasswordWidget.DEFAULT_PASSWORD_DISPLAY else: dspval = '' elif field.type == 'blob': continue else: inp = self.widgets.string.widget(field, default) xfields.append((row_id,label,inp,comment)) self.custom.dspval[fieldname] = dspval or nbsp self.custom.inpval[fieldname] = inpval or '' self.custom.widget[fieldname] = inp # if a record is provided and found, as is linkto # build a link if record and linkto: for (rtable, rfield) in table._referenced_by: if keyed: rfld = table._db[rtable][rfield] query = urllib.quote(str(rfld == record[rfld.type[10:].split('.')[1]])) else: # <block> query = urllib.quote(str(table._db[rtable][rfield] == record.id)) lname = olname = '%s.%s' % (rtable, rfield) if ofields and not olname in ofields: continue if labels and lname in labels: lname = labels[lname] widget = A(lname, _class='reference', _href='%s/%s?query=%s' % (linkto, rtable, query)) xfields.append((olname.replace('.', '__')+SQLFORM.ID_ROW_SUFFIX, '',widget,col3.get(olname,''))) self.custom.linkto[olname.replace('.', '__')] = widget # </block> # when deletable, add delete? checkbox self.custom.deletable = '' if record and deletable: widget = INPUT(_type='checkbox', _class='delete', _id=self.FIELDKEY_DELETE_RECORD, _name=self.FIELDNAME_REQUEST_DELETE, ) xfields.append((self.FIELDKEY_DELETE_RECORD+SQLFORM.ID_ROW_SUFFIX, LABEL( delete_label, _for=self.FIELDKEY_DELETE_RECORD, _id=self.FIELDKEY_DELETE_RECORD+SQLFORM.ID_LABEL_SUFFIX), widget, col3.get(self.FIELDKEY_DELETE_RECORD, ''))) self.custom.deletable = widget # when writable, add submit button self.custom.submit = '' if not readonly: widget = INPUT(_type='submit', _value=submit_button) xfields.append(('submit_record'+SQLFORM.ID_ROW_SUFFIX, '', widget,col3.get('submit_button', ''))) self.custom.submit = widget # if a record is provided and found # make sure it's id is stored in the form if record: if not self['hidden']: self['hidden'] = {} if not keyed: self['hidden']['id'] = record['id'] (begin, end) = self._xml() self.custom.begin = XML("<%s %s>" % (self.tag, begin)) self.custom.end = XML("%s</%s>" % (end, self.tag)) if formstyle == 'table3cols': table = TABLE() for id,a,b,c in xfields: td_b = self.field_parent[id] = TD(b,_class='w2p_fw') table.append(TR(TD(a,_class='w2p_fl'), td_b, TD(c,_class='w2p_fc'),_id=id)) elif formstyle == 'table2cols': table = TABLE() for id,a,b,c in xfields: td_b = self.field_parent[id] = TD(b,_class='w2p_fw',_colspan="2") table.append(TR(TD(a,_class='w2p_fl'), TD(c,_class='w2p_fc'),_id=id+'1',_class='even')) table.append(TR(td_b,_id=id+'2',_class='odd')) elif formstyle == 'divs': table = TAG['']() for id,a,b,c in xfields: div_b = self.field_parent[id] = DIV(b,_class='w2p_fw') table.append(DIV(DIV(a,_class='w2p_fl'), div_b, DIV(c,_class='w2p_fc'),_id=id)) elif formstyle == 'ul': table = UL() for id,a,b,c in xfields: div_b = self.field_parent[id] = DIV(b,_class='w2p_fw') table.append(LI(DIV(a,_class='w2p_fl'), div_b, DIV(c,_class='w2p_fc'),_id=id)) elif type(formstyle) == type(lambda:None): table = TABLE() for id,a,b,c in xfields: td_b = self.field_parent[id] = TD(b,_class='w2p_fw') newrows = formstyle(id,a,td_b,c) if type(newrows).__name__ != "tuple": newrows = [newrows] for newrow in newrows: table.append(newrow) else: raise RuntimeError, 'formsyle not supported' self.components = [table]
def __init__( self, table, record = None, deletable = False, linkto = None, upload = None, fields = None, labels = None, col3 = {}, submit_button = 'Submit', delete_label = 'Check to delete', showid = True, readonly = False, comments = True, keepopts = [], ignore_rw = False, record_id = None, formstyle = 'table3cols', buttons = ['submit'], separator = ': ', **attributes ): """ SQLFORM(db.table, record=None, fields=['name'], labels={'name': 'Your name'}, linkto=URL(f='table/db/') """ self.ignore_rw = ignore_rw self.formstyle = formstyle nbsp = XML(' ') # Firefox2 does not display fields with blanks FORM.__init__(self, *[], **attributes) ofields = fields keyed = hasattr(table,'_primarykey') # Widgets override self.widgets['string'] = StringWidget self.widgets['text'] = TextWidget # if no fields are provided, build it from the provided table # will only use writable or readable fields, unless forced to ignore if fields is None: fields = [f.name for f in table if (ignore_rw or f.writable or f.readable) and not f.compute] self.fields = fields # make sure we have an id if self.fields[0] != table.fields[0] and \ isinstance(table,Table) and not keyed: self.fields.insert(0, table.fields[0]) self.table = table # try to retrieve the indicated record using its id # otherwise ignore it if record and isinstance(record, (int, long, str, unicode)): if not str(record).isdigit(): raise HTTP(404, "Object not found") record = table._db(table._id == record).select().first() if not record: raise HTTP(404, "Object not found") self.record = record self.record_id = record_id if keyed: self.record_id = dict([(k,record and str(record[k]) or None) \ for k in table._primarykey]) self.field_parent = {} xfields = [] self.fields = fields self.custom = Storage() self.custom.dspval = Storage() self.custom.inpval = Storage() self.custom.label = Storage() self.custom.comment = Storage() self.custom.widget = Storage() self.custom.linkto = Storage() # default id field name self.id_field_name = table._id.name sep = separator or '' for fieldname in self.fields: if fieldname.find('.') >= 0: continue field = self.table[fieldname] comment = None if comments: comment = col3.get(fieldname, field.comment) if comment is None: comment = '' self.custom.comment[fieldname] = comment if not labels is None and fieldname in labels: label = labels[fieldname] else: label = field.label self.custom.label[fieldname] = label field_id = '%s_%s' % (table._tablename, fieldname) #label_is_required = SPAN(field.comment or '',_class="is_required") if field.notnull == True else '' label = LABEL(label, label and sep, _for=field_id, _id=field_id+SQLFORM.ID_LABEL_SUFFIX) row_id = field_id+SQLFORM.ID_ROW_SUFFIX if field.type == 'id': self.custom.dspval.id = nbsp self.custom.inpval.id = '' widget = '' # store the id field name (for legacy databases) self.id_field_name = field.name if record: if showid and field.name in record and field.readable: v = record[field.name] widget = SPAN(v, _id=field_id) self.custom.dspval.id = str(v) xfields.append((row_id,label, widget,comment)) self.record_id = str(record[field.name]) self.custom.widget.id = widget continue self.readonly = readonly if readonly and not ignore_rw and not field.readable: continue if record: default = record[fieldname] else: default = field.default if isinstance(default,CALLABLETYPES): default=default() cond = readonly or \ (not ignore_rw and not field.writable and field.readable) if default and not cond: default = field.formatter(default) dspval = default inpval = default if cond: # ## if field.represent is available else # ## ignore blob and preview uploaded images # ## format everything else if field.represent: inp = represent(field,default,record) elif field.type in ['blob']: continue elif field.type == 'upload': inp = UploadWidget.represent(field, default, upload) elif field.type == 'boolean': inp = self.widgets.boolean.widget(field, default, _disabled=True) else: inp = field.formatter(default) elif field.type == 'upload': if hasattr(field, 'widget') and field.widget: inp = field.widget(field, default, upload) else: inp = self.widgets.upload.widget(field, default, upload) elif hasattr(field, 'widget') and field.widget: inp = field.widget(field, default, record=record) elif field.type == 'boolean': inp = self.widgets.boolean.widget(field, default) if default: inpval = 'checked' else: inpval = '' elif OptionsWidget.has_options(field): if not field.requires.multiple: inp = self.widgets.options.widget(field, default) else: inp = self.widgets.multiple.widget(field, default) if fieldname in keepopts: inpval = TAG[''](*inp.components) elif field.type.startswith('list:'): inp = self.widgets.list.widget(field,default) elif field.type == 'text': inp = self.widgets.text.widget(field, default) elif field.type == 'password': inp = self.widgets.password.widget(field, default) if self.record: dspval = PasswordWidget.DEFAULT_PASSWORD_DISPLAY else: dspval = '' elif field.type == 'blob': continue else: field_type = widget_class.match(str(field.type)).group() field_type = field_type in self.widgets and field_type or 'string' inp = self.widgets[field_type].widget(field, default, record=record) xfields.append((row_id,label,inp,comment)) self.custom.dspval[fieldname] = dspval or nbsp self.custom.inpval[fieldname] = inpval or '' self.custom.widget[fieldname] = inp # if a record is provided and found, as is linkto # build a link if record and linkto: db = linkto.split('/')[-1] for (rtable, rfield) in table._referenced_by: if keyed: rfld = table._db[rtable][rfield] query = urllib.quote('%s.%s==%s' % (db,rfld,record[rfld.type[10:].split('.')[1]])) else: query = urllib.quote('%s.%s==%s' % (db,table._db[rtable][rfield],record[self.id_field_name])) lname = olname = '%s.%s' % (rtable, rfield) if ofields and not olname in ofields: continue if labels and lname in labels: lname = labels[lname] widget = A(lname, _class='reference', _href='%s/%s?query=%s' % (linkto, rtable, query)) xfields.append((olname.replace('.', '__')+SQLFORM.ID_ROW_SUFFIX, '',widget,col3.get(olname,''))) self.custom.linkto[olname.replace('.', '__')] = widget # </block> # when deletable, add delete? checkbox self.custom.deletable = '' if record and deletable: widget = INPUT(_type='checkbox', _class='delete', _id=self.FIELDKEY_DELETE_RECORD, _name=self.FIELDNAME_REQUEST_DELETE, ) xfields.append((self.FIELDKEY_DELETE_RECORD+SQLFORM.ID_ROW_SUFFIX, LABEL( delete_label,separator, _for=self.FIELDKEY_DELETE_RECORD, _id=self.FIELDKEY_DELETE_RECORD+SQLFORM.ID_LABEL_SUFFIX), widget, col3.get(self.FIELDKEY_DELETE_RECORD, ''))) self.custom.deletable = widget # when writable, add submit button self.custom.submit = '' if not readonly: if 'submit' in buttons: widget = self.custom.submit = INPUT(_type='submit', _value=submit_button) elif buttons: widget = self.custom.submit = DIV(*buttons) if self.custom.submit: xfields.append(('submit_record' + SQLFORM.ID_ROW_SUFFIX, '', widget, col3.get('submit_button', ''))) # if a record is provided and found # make sure it's id is stored in the form if record: if not self['hidden']: self['hidden'] = {} if not keyed: self['hidden']['id'] = record[table._id.name] (begin, end) = self._xml() self.custom.begin = XML("<%s %s>" % (self.tag, begin)) self.custom.end = XML("%s</%s>" % (end, self.tag)) table = self.createform(xfields) self.components = [table]