web.web.form

Package web :: Package web :: Module form |
[hide private]
[frames] | no frames]

# Source Code for Module web.web.form

      1  """
      2  HTML forms
      3  (part of web.py)
      4  """
      5
      6  import copy, re
      7  import webapi as web
      8  import utils, net
      9
     10 -def attrget(obj, attr, value=None):
     11      if hasattr(obj, 'has_key') and obj.has_key(attr): return obj[attr]
     12      if hasattr(obj, attr): return getattr(obj, attr)
     13      return value
     14
     15 -class Form:
     16      r"""
     17      HTML form.
     18
     19          >>> f = Form(Textbox("x"))
     20          >>> f.render()
     21          '<table>\n    <tr><th><label for="x">x</label></th><td><input type="text" id="x" name="x"/></td></tr>\n</table>'
     22      """
     23 -    def __init__(self, *inputs, **kw):
     24          self.inputs = inputs
     25          self.valid = True
     26          self.note = None
     27          self.validators = kw.pop('validators', [])
     28
     29 -    def __call__(self, x=None):
     30          o = copy.deepcopy(self)
     31          if x: o.validates(x)
     32          return o
     33
     34 -    def render(self):
     35          out = ''
     36          out += self.rendernote(self.note)
     37          out += '<table>\n'
     38
     39          for i in self.inputs:
     40              html = i.pre + i.render() + self.rendernote(i.note) + i.post
     41              if i.is_hidden():
     42                  out += '    <tr style="display: none;"><th></th><td>%s</td></tr>\n' % (html)
     43              else:
     44                  out += '    <tr><th><label for="%s">%s</label></th><td>%s</td></tr>\n' % (i.id, net.websafe(i.description), html)
     45          out += "</table>"
     46          return out
     47
     48 -    def render_css(self):
     49          out = []
     50          out.append(self.rendernote(self.note))
     51          for i in self.inputs:
     52              if not i.is_hidden():
     53                  out.append('<label for="%s">%s</label>' % (i.id, net.websafe(i.description)))
     54              out.append(i.pre)
     55              out.append(i.render())
     56              out.append(self.rendernote(i.note))
     57              out.append(i.post)
     58              out.append('\n')
     59          return ''.join(out)
     60
     61 -    def rendernote(self, note):
     62          if note: return '<strong class="wrong">%s</strong>' % net.websafe(note)
     63          else: return ""
     64
     65 -    def validates(self, source=None, _validate=True, **kw):
     66          source = source or kw or web.input()
     67          out = True
     68          for i in self.inputs:
     69              v = attrget(source, i.name)
     70              if _validate:
     71                  out = i.validate(v) and out
     72              else:
     73                  i.set_value(v)
     74          if _validate:
     75              out = out and self._validate(source)
     76              self.valid = out
     77          return out
     78
     79 -    def _validate(self, value):
     80          self.value = value
     81          for v in self.validators:
     82              if not v.valid(value):
     83                  self.note = v.msg
     84                  return False
     85          return True
     86
     87 -    def fill(self, source=None, **kw):
     88          return self.validates(source, _validate=False, **kw)
     89
     90 -    def __getitem__(self, i):
     91          for x in self.inputs:
     92              if x.name == i: return x
     93          raise KeyError, i
     94
     95 -    def __getattr__(self, name):
     96          # don't interfere with deepcopy
     97          inputs = self.__dict__.get('inputs') or []
     98          for x in inputs:
     99              if x.name == name: return x
    100          raise AttributeError, name
    101
    102 -    def get(self, i, default=None):
    103          try:
    104              return self[i]
    105          except KeyError:
    106              return default
    107
    108 -    def _get_d(self): #@@ should really be form.attr, no?
    109          return utils.storage([(i.name, i.get_value()) for i in self.inputs])
    110      d = property(_get_d)
    111
    112 -class Input(object):
    113 -    def __init__(self, name, *validators, **attrs):
    114          self.name = name
    115          self.validators = validators
    116          self.attrs = attrs = AttributeList(attrs)
    117
    118          self.description = attrs.pop('description', name)
    119          self.value = attrs.pop('value', None)
    120          self.pre = attrs.pop('pre', "")
    121          self.post = attrs.pop('post', "")
    122          self.note = None
    123
    124          self.id = attrs.setdefault('id', self.get_default_id())
    125
    126          if 'class_' in attrs:
    127              attrs['class'] = attrs['class_']
    128              del attrs['class_']
    129
    130 -    def is_hidden(self):
    131          return False
    132
    133 -    def get_type(self):
    134          raise NotImplementedError
    135
    136 -    def get_default_id(self):
    137          return self.name
    138
    139 -    def validate(self, value):
    140          self.set_value(value)
    141
    142          for v in self.validators:
    143              if not v.valid(value):
    144                  self.note = v.msg
    145                  return False
    146          return True
    147
    148 -    def set_value(self, value):
    149          self.value = value
    150
    151 -    def get_value(self):
    152          return self.value
    153
    154 -    def render(self):
    155          attrs = self.attrs.copy()
    156          attrs['type'] = self.get_type()
    157          if self.value is not None:
    158              attrs['value'] = self.value
    159          attrs['name'] = self.name
    160          return '<input %s/>' % attrs
    161
    162 -    def rendernote(self, note):
    163          if note: return '<strong class="wrong">%s</strong>' % net.websafe(note)
    164          else: return ""
    165
    166 -    def addatts(self):
    167          # add leading space for backward-compatibility
    168          return " " + str(self.attrs)
    169
    170 -class AttributeList(dict):
    171      """List of atributes of input.
    172
    173      >>> a = AttributeList(type='text', name='x', value=20)
    174      >>> a
    175      <attrs: 'type="text" name="x" value="20"'>
    176      """
    177 -    def copy(self):
    178          return AttributeList(self)
    179
    180 -    def __str__(self):
    181          return " ".join(['%s="%s"' % (k, net.websafe(v)) for k, v in self.items()])
    182
    183 -    def __repr__(self):
    184          return '<attrs: %s>' % repr(str(self))
    185
    186 -class Textbox(Input):
    187      """Textbox input.
    188
    189          >>> Textbox(name='foo', value='bar').render()
    190          '<input type="text" id="foo" value="bar" name="foo"/>'
    191          >>> Textbox(name='foo', value=0).render()
    192          '<input type="text" id="foo" value="0" name="foo"/>'
    193      """
    194 -    def get_type(self):
    195          return 'text'
    196
    197 -class Password(Input):
    198      """Password input.
    199
    200          >>> Password(name='password', value='secret').render()
    201          '<input type="password" id="password" value="secret" name="password"/>'
    202      """
    203
    204 -    def get_type(self):
    205          return 'password'
    206
    207 -class Textarea(Input):
    208      """Textarea input.
    209
    210          >>> Textarea(name='foo', value='bar').render()
    211          '<textarea id="foo" name="foo">bar</textarea>'
    212      """
    213 -    def render(self):
    214          attrs = self.attrs.copy()
    215          attrs['name'] = self.name
    216          value = net.websafe(self.value or '')
    217          return '<textarea %s>%s</textarea>' % (attrs, value)
    218
    219 -class Dropdown(Input):
    220      r"""Dropdown/select input.
    221
    222          >>> Dropdown(name='foo', args=['a', 'b', 'c'], value='b').render()
    223          '<select id="foo" name="foo">\n  <option value="a">a</option>\n  <option selected="selected" value="b">b</option>\n  <option value="c">c</option>\n</select>\n'
    224          >>> Dropdown(name='foo', args=[('a', 'aa'), ('b', 'bb'), ('c', 'cc')], value='b').render()
    225          '<select id="foo" name="foo">\n  <option value="a">aa</option>\n  <option selected="selected" value="b">bb</option>\n  <option value="c">cc</option>\n</select>\n'
    226      """
    227 -    def __init__(self, name, args, *validators, **attrs):
    228          self.args = args
    229          super(Dropdown, self).__init__(name, *validators, **attrs)
    230
    231 -    def render(self):
    232          attrs = self.attrs.copy()
    233          attrs['name'] = self.name
    234
    235          x = '<select %s>\n' % attrs
    236
    237          for arg in self.args:
    238              if isinstance(arg, (tuple, list)):
    239                  value, desc= arg
    240              else:
    241                  value, desc = arg, arg
    242
    243              if self.value == value: select_p = ' selected="selected"'
    244              else: select_p = ''
    245              x += '  <option%s value="%s">%s</option>\n' % (select_p, net.websafe(value), net.websafe(desc))
    246
    247          x += '</select>\n'
    248          return x
    249
    250 -class Radio(Input):
    251 -    def __init__(self, name, args, *validators, **attrs):
    252          self.args = args
    253          super(Radio, self).__init__(name, *validators, **attrs)
    254
    255 -    def render(self):
    256          x = '<span>'
    257          for arg in self.args:
    258              if isinstance(arg, (tuple, list)):
    259                  value, desc= arg
    260              else:
    261                  value, desc = arg, arg
    262              attrs = self.attrs.copy()
    263              attrs['name'] = self.name
    264              attrs['type'] = 'radio'
    265              attrs['value'] = arg
    266              if self.value == arg:
    267                  attrs['checked'] = 'checked'
    268              x += '<input %s/> %s' % (attrs, net.websafe(desc))
    269          x += '</span>'
    270          return x
    271
    272 -class Checkbox(Input):
    273      """Checkbox input.
    274
    275      >>> Checkbox('foo', value='bar', checked=True).render()
    276      '<input checked="checked" type="checkbox" id="foo_bar" value="bar" name="foo"/>'
    277      >>> Checkbox('foo', value='bar').render()
    278      '<input type="checkbox" id="foo_bar" value="bar" name="foo"/>'
    279      >>> c = Checkbox('foo', value='bar')
    280      >>> c.validate('on')
    281      True
    282      >>> c.render()
    283      '<input checked="checked" type="checkbox" id="foo_bar" value="bar" name="foo"/>'
    284      """
    285 -    def __init__(self, name, *validators, **attrs):
    286          self.checked = attrs.pop('checked', False)
    287          Input.__init__(self, name, *validators, **attrs)
    288
    289 -    def get_default_id(self):
    290          value = utils.safestr(self.value or "")
    291          return self.name + '_' + value.replace(' ', '_')
    292
    293 -    def render(self):
    294          attrs = self.attrs.copy()
    295          attrs['type'] = 'checkbox'
    296          attrs['name'] = self.name
    297          attrs['value'] = self.value
    298
    299          if self.checked:
    300              attrs['checked'] = 'checked'
    301          return '<input %s/>' % attrs
    302
    303 -    def set_value(self, value):
    304          self.checked = bool(value)
    305
    306 -    def get_value(self):
    307          return self.checked
    308
    309 -class Button(Input):
    310      """HTML Button.
    311
    312      >>> Button("save").render()
    313      '<button id="save" name="save">save</button>'
    314      >>> Button("action", value="save", html="<b>Save Changes</b>").render()
    315      '<button id="action" value="save" name="action"><b>Save Changes</b></button>'
    316      """
    317 -    def __init__(self, name, *validators, **attrs):
    318          super(Button, self).__init__(name, *validators, **attrs)
    319          self.description = ""
    320
    321 -    def render(self):
    322          attrs = self.attrs.copy()
    323          attrs['name'] = self.name
    324          if self.value is not None:
    325              attrs['value'] = self.value
    326          html = attrs.pop('html', None) or net.websafe(self.name)
    327          return '<button %s>%s</button>' % (attrs, html)
    328
    329 -class Hidden(Input):
    330      """Hidden Input.
    331
    332          >>> Hidden(name='foo', value='bar').render()
    333          '<input type="hidden" id="foo" value="bar" name="foo"/>'
    334      """
    335 -    def is_hidden(self):
    336          return True
    337
    338 -    def get_type(self):
    339          return 'hidden'
    340
    341 -class File(Input):
    342      """File input.
    343
    344          >>> File(name='f').render()
    345          '<input type="file" id="f" name="f"/>'
    346      """
    347 -    def get_type(self):
    348          return 'file'
    349
    350 -class Validator:
    351 -    def __deepcopy__(self, memo): return copy.copy(self)
    352 -    def __init__(self, msg, test, jstest=None): utils.autoassign(self, locals())
    353 -    def valid(self, value):
    354          try: return self.test(value)
    355          except: return False
    356
    357  notnull = Validator("Required", bool)
    358
    359 -class regexp(Validator):
    360 -    def __init__(self, rexp, msg):
    361          self.rexp = re.compile(rexp)
    362          self.msg = msg
    363
    364 -    def valid(self, value):
    365          return bool(self.rexp.match(value))
    366
    367  if __name__ == "__main__":
    368      import doctest
    369      doctest.testmod()
    370