Source code for pytomo.web.form

"""
HTML forms
(part of web.py)
"""

import copy, re
import webapi as web
import utils, net

[docs]def attrget(obj, attr, value=None): try: if hasattr(obj, 'has_key') and obj.has_key(attr): return obj[attr] except TypeError: # Handle the case where has_key takes different number of arguments. # This is the case with Model objects on appengine. See #134 pass if hasattr(obj, attr): return getattr(obj, attr) return value
[docs]class Form(object): r""" HTML form. >>> f = Form(Textbox("x")) >>> f.render() u'<table>\n <tr><th><label for="x">x</label></th><td><input type="text" id="x" name="x"/></td></tr>\n</table>' """ def __init__(self, *inputs, **kw): self.inputs = inputs self.valid = True self.note = None self.validators = kw.pop('validators', []) def __call__(self, x=None): o = copy.deepcopy(self) if x: o.validates(x) return o
[docs] def render(self): out = '' out += self.rendernote(self.note) out += '<table>\n' for i in self.inputs: html = utils.safeunicode(i.pre) + i.render() + self.rendernote(i.note) + utils.safeunicode(i.post) if i.is_hidden(): out += ' <tr style="display: none;"><th></th><td>%s</td></tr>\n' % (html) else: out += ' <tr><th><label for="%s">%s</label></th><td>%s</td></tr>\n' % (i.id, net.websafe(i.description), html) out += "</table>" return out
[docs] def render_css(self): out = [] out.append(self.rendernote(self.note)) for i in self.inputs: if not i.is_hidden(): out.append('<label for="%s">%s</label>' % (i.id, net.websafe(i.description))) out.append(i.pre) out.append(i.render()) out.append(self.rendernote(i.note)) out.append(i.post) out.append('\n') return ''.join(out)
[docs] def rendernote(self, note): if note: return '<strong class="wrong">%s</strong>' % net.websafe(note) else: return ""
[docs] def validates(self, source=None, _validate=True, **kw): source = source or kw or web.input() out = True for i in self.inputs: v = attrget(source, i.name) if _validate: out = i.validate(v) and out else: i.set_value(v) if _validate: out = out and self._validate(source) self.valid = out return out
def _validate(self, value): self.value = value for v in self.validators: if not v.valid(value): self.note = v.msg return False return True
[docs] def fill(self, source=None, **kw): return self.validates(source, _validate=False, **kw)
def __getitem__(self, i): for x in self.inputs: if x.name == i: return x raise KeyError, i def __getattr__(self, name): # don't interfere with deepcopy inputs = self.__dict__.get('inputs') or [] for x in inputs: if x.name == name: return x raise AttributeError, name
[docs] def get(self, i, default=None): try: return self[i] except KeyError: return default
def _get_d(self): #@@ should really be form.attr, no? return utils.storage([(i.name, i.get_value()) for i in self.inputs]) d = property(_get_d)
[docs]class Input(object): def __init__(self, name, *validators, **attrs): self.name = name self.validators = validators self.attrs = attrs = AttributeList(attrs) self.description = attrs.pop('description', name) self.value = attrs.pop('value', None) self.pre = attrs.pop('pre', "") self.post = attrs.pop('post', "") self.note = None self.id = attrs.setdefault('id', self.get_default_id()) if 'class_' in attrs: attrs['class'] = attrs['class_'] del attrs['class_']
[docs] def is_hidden(self): return False
[docs] def get_type(self): raise NotImplementedError
[docs] def get_default_id(self): return self.name
[docs] def validate(self, value): self.set_value(value) for v in self.validators: if not v.valid(value): self.note = v.msg return False return True
[docs] def set_value(self, value): self.value = value
[docs] def get_value(self): return self.value
[docs] def render(self): attrs = self.attrs.copy() attrs['type'] = self.get_type() if self.value is not None: attrs['value'] = self.value attrs['name'] = self.name return '<input %s/>' % attrs
[docs] def rendernote(self, note): if note: return '<strong class="wrong">%s</strong>' % net.websafe(note) else: return ""
[docs] def addatts(self): # add leading space for backward-compatibility return " " + str(self.attrs)
[docs]class AttributeList(dict): """List of atributes of input. >>> a = AttributeList(type='text', name='x', value=20) >>> a <attrs: 'type="text" name="x" value="20"'> """
[docs] def copy(self): return AttributeList(self)
def __str__(self): return " ".join(['%s="%s"' % (k, net.websafe(v)) for k, v in self.items()]) def __repr__(self): return '<attrs: %s>' % repr(str(self))
[docs]class Textbox(Input): """Textbox input. >>> Textbox(name='foo', value='bar').render() u'<input type="text" id="foo" value="bar" name="foo"/>' >>> Textbox(name='foo', value=0).render() u'<input type="text" id="foo" value="0" name="foo"/>' """
[docs] def get_type(self): return 'text'
[docs]class Password(Input): """Password input. >>> Password(name='password', value='secret').render() u'<input type="password" id="password" value="secret" name="password"/>' """
[docs] def get_type(self): return 'password'
[docs]class Textarea(Input): """Textarea input. >>> Textarea(name='foo', value='bar').render() u'<textarea id="foo" name="foo">bar</textarea>' """
[docs] def render(self): attrs = self.attrs.copy() attrs['name'] = self.name value = net.websafe(self.value or '') return '<textarea %s>%s</textarea>' % (attrs, value)
[docs]class GroupedDropdown(Dropdown): r"""Grouped Dropdown/select input. >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', ('Volvo', 'Saab')), ('German Cars', ('Mercedes', 'Audi'))), value='Audi').render() u'<select id="car_type" name="car_type">\n <optgroup label="Swedish Cars">\n <option value="Volvo">Volvo</option>\n <option value="Saab">Saab</option>\n </optgroup>\n <optgroup label="German Cars">\n <option value="Mercedes">Mercedes</option>\n <option selected="selected" value="Audi">Audi</option>\n </optgroup>\n</select>\n' >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', (('v', 'Volvo'), ('s', 'Saab'))), ('German Cars', (('m', 'Mercedes'), ('a', 'Audi')))), value='a').render() u'<select id="car_type" name="car_type">\n <optgroup label="Swedish Cars">\n <option value="v">Volvo</option>\n <option value="s">Saab</option>\n </optgroup>\n <optgroup label="German Cars">\n <option value="m">Mercedes</option>\n <option selected="selected" value="a">Audi</option>\n </optgroup>\n</select>\n' """ def __init__(self, name, args, *validators, **attrs): self.args = args super(Dropdown, self).__init__(name, *validators, **attrs)
[docs] def render(self): attrs = self.attrs.copy() attrs['name'] = self.name x = '<select %s>\n' % attrs for label, options in self.args: x += ' <optgroup label="%s">\n' % net.websafe(label) for arg in options: x += self._render_option(arg, indent = ' ') x += ' </optgroup>\n' x += '</select>\n' return x
[docs]class Radio(Input): def __init__(self, name, args, *validators, **attrs): self.args = args super(Radio, self).__init__(name, *validators, **attrs)
[docs] def render(self): x = '<span>' for arg in self.args: if isinstance(arg, (tuple, list)): value, desc= arg else: value, desc = arg, arg attrs = self.attrs.copy() attrs['name'] = self.name attrs['type'] = 'radio' attrs['value'] = value if self.value == value: attrs['checked'] = 'checked' x += '<input %s/> %s' % (attrs, net.websafe(desc)) x += '</span>' return x
[docs]class Checkbox(Input): """Checkbox input. >>> Checkbox('foo', value='bar', checked=True).render() u'<input checked="checked" type="checkbox" id="foo_bar" value="bar" name="foo"/>' >>> Checkbox('foo', value='bar').render() u'<input type="checkbox" id="foo_bar" value="bar" name="foo"/>' >>> c = Checkbox('foo', value='bar') >>> c.validate('on') True >>> c.render() u'<input checked="checked" type="checkbox" id="foo_bar" value="bar" name="foo"/>' """ def __init__(self, name, *validators, **attrs): self.checked = attrs.pop('checked', False) Input.__init__(self, name, *validators, **attrs)
[docs] def get_default_id(self): value = utils.safestr(self.value or "") return self.name + '_' + value.replace(' ', '_')
[docs] def render(self): attrs = self.attrs.copy() attrs['type'] = 'checkbox' attrs['name'] = self.name attrs['value'] = self.value if self.checked: attrs['checked'] = 'checked' return '<input %s/>' % attrs
[docs] def set_value(self, value): self.checked = bool(value)
[docs] def get_value(self): return self.checked
[docs]class Button(Input): """HTML Button. >>> Button("save").render() u'<button id="save" name="save">save</button>' >>> Button("action", value="save", html="<b>Save Changes</b>").render() u'<button id="action" value="save" name="action"><b>Save Changes</b></button>' """ def __init__(self, name, *validators, **attrs): super(Button, self).__init__(name, *validators, **attrs) self.description = ""
[docs] def render(self): attrs = self.attrs.copy() attrs['name'] = self.name if self.value is not None: attrs['value'] = self.value html = attrs.pop('html', None) or net.websafe(self.name) return '<button %s>%s</button>' % (attrs, html)
[docs]class Hidden(Input): """Hidden Input. >>> Hidden(name='foo', value='bar').render() u'<input type="hidden" id="foo" value="bar" name="foo"/>' """
[docs] def is_hidden(self): return True
[docs] def get_type(self): return 'hidden'
[docs]class File(Input): """File input. >>> File(name='f').render() u'<input type="file" id="f" name="f"/>' """
[docs] def get_type(self): return 'file'
[docs]class Validator: def __deepcopy__(self, memo): return copy.copy(self) def __init__(self, msg, test, jstest=None): utils.autoassign(self, locals())
[docs] def valid(self, value): try: return self.test(value) except: return False
notnull = Validator("Required", bool)
[docs]class regexp(Validator): def __init__(self, rexp, msg): self.rexp = re.compile(rexp) self.msg = msg
[docs] def valid(self, value): return bool(self.rexp.match(value))
if __name__ == "__main__": import doctest doctest.testmod()