Skip to content

Loading…

Add FormModel + validation #20

Open
wants to merge 4 commits into from

1 participant

Commits on May 31, 2012
  1. @datnguyen0606

    add formmodel for warp

    datnguyen0606 committed
Commits on Jun 4, 2012
  1. @datnguyen0606
Commits on Jun 8, 2012
  1. @datnguyen0606
Commits on Jun 29, 2012
  1. @datnguyen0606

    update formmodel

    datnguyen0606 committed
Showing with 379 additions and 29 deletions.
  1. +129 −29 warp/crud/colproxy.py
  2. 0 warp/forms/__init__.py
  3. +116 −0 warp/forms/model.py
  4. 0 warp/forms/render.py
  5. +134 −0 warp/forms/validators.py
View
158 warp/crud/colproxy.py 100644 → 100755
@@ -1,16 +1,21 @@
import pytz, operator, re
from datetime import datetime, date
-
-from warp.runtime import internal, templateLookup, exposedStormClasses
+import itertools
+from warp.runtime import internal, store, templateLookup, exposedStormClasses
from warp.helpers import url, link, getNode, renderTemplateObj, getCrudClass, getCrudObj, getCrudNode
-
+from warp.forms import validators
+import cgi
class BaseProxy(object):
- def __init__(self, obj, col):
+ _formfield = True
+ errors = list()
+
+ def __init__(self, obj, col, valid=[]):
self.obj = obj
self.col = col
-
+ self.validators = valid
+
def fieldName(self):
id = self.obj.fakeID if hasattr(self.obj, 'fakeID') else self.obj.id
@@ -28,13 +33,94 @@ def render_edit(self, request):
self.fieldName(),
getattr(self.obj, self.col) or "")
+ def render_inputs(self, val=None):
+ fieldName = "%s-%s-%s" % (self.obj.__class__.__name__,
+ self.obj.id, self.col)
+ return u'<input type="text" name="%s" value="%s" />' % (
+ fieldName, val)
+
+ def render_label(self):
+ return u'<label for="%s">%s</label>' % (self.col, self.col.title())
+
+ def render_errors(self):
+ error = ""
+ if len(self.errors) > 0:
+ error = u'<span class="error">%s</span>' % (self.errors[0])
+ return error
+
+ def render_field(self, val=None):
+ return u'%s %s %s' % (
+ self.render_label(), self.render_inputs(val), self.render_errors())
def save(self, val, request):
try:
setattr(self.obj, self.col, val)
except (TypeError, ValueError):
- return u"Invalid value"
-
+ return u"Invalid value"
+
+ def gettext(self, string):
+ return string
+
+ def validate(self, val, request, extra_validators=tuple()):
+ self.errors = []
+ for validator in itertools.chain(self.validators, extra_validators):
+ error = validator.validate(val, request)
+ print error
+ if error:
+ self.errors.append(error)
+ if self.errors != []:
+ return (False, self.errors[0])
+ return (True, None)
+
+
+class FileUploadProxy(BaseProxy):
+
+ jsTemplate = """
+<script type="text/javascript">
+jQuery(document).ready(function($) { var form = $("input[name='%s']")[0].form;
+ $(form).attr("enctype", "multipart/form-data");
+});
+</script>
+"""
+ type = None
+ size = None
+ def __init__(self, obj=None, col=None, valid=[]):
+ super(FileUploadProxy, self).__init__(obj, col, valid=valid)
+
+ def render_inputs(self, val=None):
+ fieldName = "%s-%s-%s" % (self.obj.__class__.__name__,
+ self.obj.id, self.col)
+ return u'<input name="%s" type="file" />' % (fieldName)
+
+ def validate(self, val, request, extra_validators=tuple()):
+ fieldName = "%s-%s-%s" % (self.obj.__class__.__name__, self.obj.id, self.col)
+
+ # twistd.web request cannot get file's information
+ headers = request.getAllHeaders()
+ f = cgi.FieldStorage(
+ fp = request.content,
+ headers = headers,
+ environ = {'REQUEST_METHOD':'POST',
+ 'CONTENT_TYPE': headers['content-type'],
+ }
+ )
+
+ # cgi does not provide function to get file size
+ f[fieldName].file.seek(0, 2)
+ filesize = f[fieldName].file.tell()
+ f[fieldName].file.seek(0)
+
+ self.type = f[fieldName].type
+ self.size = filesize*1.0/1000 #kb
+
+ self.errors = []
+ for validator in itertools.chain(self.validators, extra_validators):
+ error = validator.validate(self.type, self.size, request)
+ if error:
+ self.errors.append(error)
+ if self.errors != []:
+ return (False, self.errors[0])
+ return (True, None)
class StringProxy(BaseProxy):
@@ -43,9 +129,10 @@ class StringProxy(BaseProxy):
class RawStringProxy(BaseProxy):
- def __init__(self, obj, col, encoding="utf8"):
+ def __init__(self, obj=None, col=None, encoding="utf8", valid=[]):
self.obj = obj
self.col = col
+ self.validators = valid
self.encoding = encoding
def render_view(self, request):
@@ -63,20 +150,22 @@ def save(self, val, request):
return u"Invalid value"
-
class NonEmptyStringProxy(StringProxy):
+ def __init__(self, obj=None, col=None, valid=[]):
+ super(NonEmptyStringProxy, self).__init__(obj, col, valid=valid)
def save(self, val, request):
if not val:
return u"Cannot be empty"
super(NonEmptyStringProxy, self).save(val, request)
+ def validate(self, val, request):
+ return super(NonEmptyStringProxy, self).validate(val, request, [validators.Required()])
class AreaProxy(StringProxy):
-
- def __init__(self, obj, col, rows=6, cols=80):
- super(AreaProxy, self).__init__(obj, col)
+ def __init__(self, obj=None, col=None, rows=6, cols=80, valid=[]):
+ super(AreaProxy, self).__init__(obj, col, valid)
self.rows = rows
self.cols = cols
@@ -86,20 +175,28 @@ def render_view(self, request):
def render_edit(self, request):
return u'<textarea name="warpform-%s" cols="%s" rows="%s">%s</textarea>' % (
self.fieldName(), self.cols, self.rows,
- getattr(self.obj, self.col) or '')
+ getattr(self.obj, self.col))
+ def render_inputs(self, val=None):
+ fieldName = "%s-%s-%s" % (self.obj.__class__.__name__,
+ self.obj.id, self.col)
+ return u'<textarea name="%s" cols="%s" rows="%s">%s</textarea>' % (
+ fieldName, self.cols, self.rows, val)
+
class HTMLAreaProxy(StringProxy):
def render_edit(self, request):
return u'<textarea name="warpform-%s" cols="80" rows="20" class="markItUp">%s</textarea>' % (
self.fieldName(),
- getattr(self.obj, self.col) or '')
-
+ getattr(self.obj, self.col))
class BooleanProxy(BaseProxy):
+ def __init__(self, obj=None, col=None, valid=[]):
+ super(BooleanProxy, self).__init__(obj, col, valid=valid)
+
def render_view(self, request):
return u"True" if getattr(self.obj, self.col) else u"False"
@@ -113,6 +210,16 @@ def render_edit(self, request):
return u'<input type="checkbox" name="warpform-%s" class="warpform-bool" value="%s" %s/>' % (
self.fieldName(), val, checkedBit)
+ def render_inputs(self, val=None):
+ fieldName = "%s-%s-%s" % (self.obj.__class__.__name__,
+ self.obj.id, self.col)
+ if val:
+ checkedBit = 'checked="checked" '
+ else:
+ checkedBit = ''
+
+ return u'<input type="checkbox" name="%s" class="warpform-bool" value="%s" %s/>' % (
+ fieldName, val, checkedBit)
class IntProxy(BaseProxy):
@@ -228,23 +335,17 @@ class DateProxy(BaseProxy):
dateFormat = "%m/%d/%Y"
timezone = pytz.UTC
- def to_db(self, val):
- return val
-
- def from_db(self, val):
- return val
-
def render_view(self, request):
val = getattr(self.obj, self.col)
if val is None: return u"[None]"
- return self.from_db(val).strftime(self.dateFormat)
+ return val.strftime(self.dateFormat)
def render_edit(self, request):
fieldName = self.fieldName()
val = getattr(self.obj, self.col)
dateField = u'<input type="text" name="warpform-%s" id="date-field-%s" class="warpform-date" value="%s" size="10" />' % (
- fieldName, fieldName, self.from_db(val).strftime("%m/%d/%Y") if val else "")
+ fieldName, fieldName, val.strftime("%m/%d/%Y") if val else "")
return u"%s %s" % (dateField, self.jsTemplate % fieldName)
@@ -266,9 +367,9 @@ def save(self, val, request):
# .astimezone(pytz.UTC)
.date())
except ValueError:
- return u"Value '%s' didn't match format '%s'" % (val, self.dateFormat)
+ return u"Value '%s' didn't match format '%m/%d/%Y'" % (val, self.dateFormat)
- setattr(self.obj, self.col, self.to_db(date))
+ setattr(self.obj, self.col, date)
class DateTimeProxy(DateProxy):
@@ -419,12 +520,11 @@ def render_edit(self, request):
crudClass = getCrudClass(refClass)
if self.col in noEdit or idCol in noEdit:
- obj = request.store.get(refClass, objID)
+ obj = store.get(refClass, objID)
return '<input type="hidden" name="warpform-%s" value="%s" />%s' % (
self.fieldName(), objID, crudClass(obj).name(request))
- allObjs = [(crudClass(o).name(request), o)
- for o in request.store.find(refClass, *self.conditions)]
+ allObjs = [(crudClass(o).name(request), o) for o in store.find(refClass, *self.conditions)]
allObjs.sort()
if objID is None:
@@ -461,7 +561,7 @@ def save(self, val, request):
refClass = self.obj.__class__.__dict__[self.col]._relation.remote_cls
if val is not None:
- obj = request.store.get(refClass, val)
+ obj = store.get(refClass, val)
if obj is None:
return u"No such object (id %s)" % val
val = obj.id
View
0 warp/forms/__init__.py
No changes.
View
116 warp/forms/model.py
@@ -0,0 +1,116 @@
+from warp.crud import colproxy, columns
+from warp import helpers
+
+
+class BoundField(object):
+ def __init__(self, field, data):
+ self.field = field
+ self.data = data
+
+ def render_field(self):
+ return self.field.render_field(self.data)
+
+ def render_label(self):
+ return self.field.render_label()
+
+ def render_inputs(self):
+ return self.field.render_inputs(self.data)
+
+ def render_errors(self):
+ return self.field.render_errors()
+
+ def set_data(self, data):
+ self.data = data
+
+ def get_data(self):
+ return self.data
+
+ def validate(self, request):
+ return self.field.validate(self.data, request)
+
+
+class FormModel(object):
+
+ def __init__(self, id=1, fields={}):
+ self.id = id
+ self.field_value = fields
+
+ _fields = {}
+ template = None
+
+ def _build_field(self, name):
+ if name in self._fields.keys():
+ return self._fields[name]
+
+ unbound_field = getattr(self, name)
+ if not isinstance(unbound_field, colproxy.BaseProxy):
+ raise TypeError("Invalid type")
+
+ field = unbound_field.__class__(self, name, valid=unbound_field.validators)
+ bound_field = BoundField(field, self.field_value[name] if name in self.field_value.keys() else None)
+ self._fields[name] = bound_field
+
+ return bound_field
+
+ def render(self, request):
+ return helpers.renderLocalTemplate(request, self.template, form=self)
+
+ def errors(self, name):
+ return self._build_field(name).render_errors()
+
+ def inputs(self, name):
+ return self._build_field(name).render_inputs()
+
+ def label(self, name):
+ return self._build_field(name).render_label()
+
+ def widget(self, name):
+ return self._build_field(name).render_field()
+
+ def process(self, request):
+ for name, field in self._fields.iteritems():
+ fieldName = "%s-%s-%s" % (self.__class__.__name__, self.id, name)
+ if fieldName in request.args.keys():
+ field.data = request.args.get(fieldName)[0]
+
+ def validate(self, request):
+ self.process(request)
+ errors = []
+ success = None
+ for name, field in self._fields.iteritems():
+ (success, error) = field.validate(request)
+ if not success:
+ errors.append(error)
+
+ if errors:
+ return (False, errors)
+ return (True, errors)
+
+
+class FormSet(object):
+ subforms = []
+ def __init__(self, id='1', formModel=None, entries=None):
+ self.id = id
+ if formModel:
+ self.template = formModel.template
+ self.expose(formModel, entries)
+
+ template = None
+ def render(self, request):
+ return helpers.renderLocalTemplate(request, self.template, forms=self)
+
+ def expose(self, FormModel, entries):
+ self.subforms[:] = []
+
+ for idx, entry in enumerate(entries):
+ _form = FormModel('n'+str(idx))
+ for name, field in _form._fields.iteritems():
+ setattr(field, "data", getattr(entry, name) if hasattr(entry, name) else "")
+ self.subforms.append(_form)
+
+ def validate(self, request, extra_validators=None):
+ success = True
+ for form in self.subforms:
+ t = form.validate(request, extra_validators)
+ success = success and t
+ return success
View
0 warp/forms/render.py
No changes.
View
134 warp/forms/validators.py
@@ -0,0 +1,134 @@
+import re
+
+
+class Length(object):
+ def __init__(self, min=-1, max=-1, message=None):
+ assert min != -1 or max!=-1, 'At least one of `min` or `max` must be specified.'
+ assert max == -1 or min <= max, '`min` cannot be more than `max`.'
+ self.min = min
+ self.max = max
+ self.message = message
+
+ def validate(self, val, request):
+ l = val and len(val) or 0
+ if l < self.min or self.max != -1 and l > self.max:
+ if self.message is None:
+ if self.max == -1:
+ self.message = u'Field must be at least %(min)d character long.'
+ elif self.min == -1:
+ self.message = u'Field cannot be longer than %(max)d character.'
+ else:
+ self.message = u'Field must be between %(min)d and %(max)d characters long.'
+
+ return self.message % dict(min=self.min, max=self.max)
+ return None
+
+
+
+class File(object):
+ def __init__(self, extensions=None, size=None, message="Invalid file"):
+ self.message = message
+ self.extensions = extensions
+ self.size = size
+
+ def validate(self, type, size, request):
+ if not type or type not in self.extensions:
+ return self.message
+ if not size or size > self.size:
+ return self.message
+ return None
+
+class Required(object):
+
+ def __init__(self, message=None):
+ self.message = message
+
+ def validate(self, val, request):
+ if not val or isinstance(val, basestring) and not val.strip():
+ if self.message is None:
+ self.message = u'This field is required.'
+
+ return self.message
+ return None
+
+class Regexp(object):
+
+ def __init__(self, regex, flags=0, message=None):
+ if isinstance(regex, basestring):
+ regex = re.compile(regex, flags)
+ self.regex = regex
+ self.message = message
+
+ def validate(self, val, request):
+ if not self.regex.match(val or u''):
+ if self.message is None:
+ self.message = u'Invalid input.'
+
+ return self.message
+ return None
+
+
+class Email(Regexp):
+
+ def __init__(self, message=None):
+ super(Email, self).__init__(r'^.+@[^.].*\.[a-z]{2,10}$', re.IGNORECASE, message)
+
+ def validate(self, val, request):
+ if self.message is None:
+ self.message = u'Invalid email address.'
+
+ return super(Email, self).validate(val, request)
+
+
+class IPAddress(Regexp):
+
+ def __init__(self, message=None):
+ super(IPAddress, self).__init__(r'^([0-9]{1,3}\.){3}[0-9]{1,3}$', message=message)
+
+ def validate(self, val, request):
+ if self.message is None:
+ self.message = field.gettext(u'Invalid IP address.')
+
+ return super(IPAddress, self).validate(val, request)
+
+
+class MacAddress(Regexp):
+
+ def __init__(self, message=None):
+ pattern = r'^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$'
+ super(MacAddress, self).__init__(pattern, message=message)
+
+ def validate(self, val, request):
+ if self.message is None:
+ self.message = field.gettext(u'Invalid Mac address.')
+
+ return super(MacAddress, self).validate(val, request)
+
+
+class URL(Regexp):
+
+ def __init__(self, require_tld=True, message=None):
+ tld_part = (require_tld and ur'\.[a-z]{2,10}' or u'')
+ regex = ur'^[a-z]+://([^/:]+%s|([0-9]{1,3}\.){3}[0-9]{1,3})(:[0-9]+)?(\/.*)?$' % tld_part
+ super(URL, self).__init__(regex, re.IGNORECASE, message)
+
+ def validate(self, val, request):
+ if self.message is None:
+ self.message = field.gettext(u'Invalid URL.')
+
+ return super(URL, self).validate(val, request)
+
+
+class UUID(Regexp):
+
+ def __init__(self, message=None):
+ pattern = r'^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$'
+ super(UUID, self).__init__(pattern, message=message)
+
+ def validate(self, val, request):
+ if self.message is None:
+ self.message = field.gettext(u'Invalid UUID.')
+
+ return super(UUID, self).validate(val, request)
+
+
Something went wrong with that request. Please try again.