import cgi import field class form(object): """ a former form usually all you need to do is create a form with some instances of fields (see field.py) passed to it, and call 'form.render(data)', where data is a cgi.FieldStorage instance or a dict, to have your form both validated and rendered - the function returns a tuple (html, errors), where errors is None """ submitlabel = 'submit' def __init__(self, id, fields, action='.'): self.id = id self.fields = fields self.action = action def render(self, data=None): """ quick way to render a form, though it doesn't return the clean data """ if data is not None and not data.has_key(self.id): # we always expect the submit button in the fs data = None errors = [] ret = [] for field in self.fields: html, error = field.render(data) if error: errors.append((field, error)) ret.append(html) ret.insert(0, self.render_open(errors)) ret.append(self.render_close(errors)) return '\n'.join(ret), errors def process_data(self, data=None): """ return the validated/normalized data and a list of errors (if any) data is a dict or FieldStorage instance """ if data is None or not data.has_key(self.id): return None, [] errors = [] ret = {} for field in self.fields: value, error = field.get_value(data) ret[field.id] = value if error: errors.append((field, error)) return ret, errors def render_validated(self, data, errors): """ render the form with data that has already validated """ ret = [self.render_open(errors)] errors = dict(errors) for field in self.fields: value = None if data is not None: value = data[field.id] ret.append(field.render_validated( value, errors.get(field))) ret.append(self.render_close(errors)) return '\n'.join(ret) def render_open(self, errors): """ mostly internal - render the open tag """ enctype = self.get_enctype() onsubmitdata = self.get_onsubmit() onsubmit = '' if onsubmitdata: if not isinstance(onsubmitdata, unicode): onsubmitdata = unicode(onsubmitdata) onsubmit = ' onsubmit="%s"' % (onsubmitdata,) return '
' % ( self.action, self.id, enctype, onsubmit) def render_close(self, errors): """ mostly internal - render the close tag """ label = self.submitlabel if not isinstance(label, unicode): label = unicode(label) return '\n'.join([ '
', '' % (self.id, label), '
', '
', ]) def add_field(self, field): """ add a field to the form """ self.fields.append(field) def get_fields(self): """ yield all the form fields """ for field in self.fields: yield field def get_field(self, id): for field in self.fields: if field.id == id: return field def get_enctype(self): default = 'application/x-www-form-urlencoded' for field in self.fields: if field.enctype != default: return field.enctype return default def get_scripts(self): scripts = {} for field in self.fields: for script in field.scripts: scripts[script] = True return scripts.keys() def get_onload(self): onloads = {} for field in self.fields: onload = field.onload.strip() if onload: if not onload.endswith(';'): onload += ';' onloads[onload] = True return ' '.join(onloads) def get_onsubmit(self): onsubmits = [] for field in self.fields: onsubmit = field.onsubmit.strip() if onsubmit: if not onsubmit.endswith(';'): onsubmit += ';' if onsubmit not in onsubmits: onsubmits.append(onsubmit) return ' '.join(onsubmits) def get_onunload(self): onunloads = {} for field in self.fields: onunload = field.onunload.strip() if onunload: if not onunload.endswith(';'): onunload += ';' onunloads[onunload] = True return ' '.join(onunloads) def copy(self): f = form(self.id, [], self.action) for field in self.fields: f.add_field(field.copy()) return f