Merge pull request #1293 from leonelcamara/fix_1266

fixes #1266 and adds tests to make sure it doesn't happen again
This commit is contained in:
mdipierro
2016-04-13 22:15:53 -05:00
2 changed files with 42 additions and 26 deletions
+12
View File
@@ -232,7 +232,19 @@ class TestValidators(unittest.TestCase):
self.assertEqual(sorted(rtn), [('%d' % george_id, 'george'), ('%d' % costanza_id, 'costanza')]) self.assertEqual(sorted(rtn), [('%d' % george_id, 'george'), ('%d' % costanza_id, 'costanza')])
rtn = IS_IN_DB(db, db.person.id, db.person.name, error_message='oops', sort=True).options(zero=True) rtn = IS_IN_DB(db, db.person.id, db.person.name, error_message='oops', sort=True).options(zero=True)
self.assertEqual(rtn, [('', ''), ('%d' % costanza_id, 'costanza'), ('%d' % george_id, 'george')]) self.assertEqual(rtn, [('', ''), ('%d' % costanza_id, 'costanza'), ('%d' % george_id, 'george')])
# Test it works with self reference
db.define_table('category',
Field('parent_id', 'reference category', requires=IS_EMPTY_OR(IS_IN_DB(db, 'category.id', '%(name)s'))),
Field('name')
)
ret = db.category.validate_and_insert(name='seinfeld')
self.assertFalse(list(ret.errors))
ret = db.category.validate_and_insert(name='characters', parent_id=ret.id)
self.assertFalse(list(ret.errors))
rtn = IS_IN_DB(db, 'category.id', '%(name)s')(ret.id)
self.assertEqual(rtn, (ret.id, None))
db.person.drop() db.person.drop()
db.category.drop()
def test_IS_NOT_IN_DB(self): def test_IS_NOT_IN_DB(self):
from gluon.dal import DAL, Field from gluon.dal import DAL, Field
+30 -26
View File
@@ -376,8 +376,8 @@ class IS_JSON(Validator):
def __call__(self, value): def __call__(self, value):
try: try:
if self.native_json: if self.native_json:
simplejson.loads(value) # raises error in case of malformed json simplejson.loads(value) # raises error in case of malformed json
return (value, None) # the serialized value is not passed return (value, None) # the serialized value is not passed
else: else:
return (simplejson.loads(value), None) return (simplejson.loads(value), None)
except JSONErrors: except JSONErrors:
@@ -459,7 +459,7 @@ class IS_IN_SET(Validator):
def __call__(self, value): def __call__(self, value):
if self.multiple: if self.multiple:
### if below was values = re.compile("[\w\-:]+").findall(str(value)) # if below was values = re.compile("[\w\-:]+").findall(str(value))
if not value: if not value:
values = [] values = []
elif isinstance(value, (tuple, list)): elif isinstance(value, (tuple, list)):
@@ -523,8 +523,8 @@ class IS_IN_DB(Validator):
field = field._id field = field._id
elif isinstance(field, str): elif isinstance(field, str):
items = field.split('.') items = field.split('.')
if len(items)==1: items+=['id'] if len(items) == 1:
field = self.dbset.db[items[0]][items[1]] field = items[0] + '.id'
(ktable, kfield) = str(field).split('.') (ktable, kfield) = str(field).split('.')
if not label: if not label:
@@ -534,16 +534,16 @@ class IS_IN_DB(Validator):
label = '%%(%s)s' % str(label).split('.')[-1] label = '%%(%s)s' % str(label).split('.')[-1]
fieldnames = regex2.findall(label) fieldnames = regex2.findall(label)
if kfield not in fieldnames: if kfield not in fieldnames:
fieldnames.append(kfield) # kfield must be last fieldnames.append(kfield) # kfield must be last
elif isinstance(label, Field): elif isinstance(label, Field):
fieldnames = [label.name, kfield] # kfield must be last fieldnames = [label.name, kfield] # kfield must be last
label = '%%(%s)s' % label.name label = '%%(%s)s' % label.name
elif callable(label): elif callable(label):
fieldnames = '*' fieldnames = '*'
else: else:
raise NotImplementedError raise NotImplementedError
self.field = field # the lookup field
self.fieldnames = fieldnames # fields requires to build the formatting self.fieldnames = fieldnames # fields requires to build the formatting
self.label = label self.label = label
self.ktable = ktable self.ktable = ktable
self.kfield = kfield self.kfield = kfield
@@ -621,16 +621,16 @@ class IS_IN_DB(Validator):
if isinstance(value, list): if isinstance(value, list):
values = value values = value
elif self.delimiter: elif self.delimiter:
values = value.split(self.delimiter) # because of autocomplete values = value.split(self.delimiter) # because of autocomplete
elif value: elif value:
values = [value] values = [value]
else: else:
values = [] values = []
if self.field.type in ('id','integer'): if field.type in ('id', 'integer'):
new_values = [] new_values = []
for value in values: for value in values:
if not (isinstance(value,(int,long)) or value.isdigit()): if not (isinstance(value, (int, long)) or value.isdigit()):
if self.auto_add: if self.auto_add:
value = str(self.maybe_add(table, self.fieldnames[0], value)) value = str(self.maybe_add(table, self.fieldnames[0], value))
else: else:
@@ -657,8 +657,8 @@ class IS_IN_DB(Validator):
elif count(values) == len(values): elif count(values) == len(values):
return (values, None) return (values, None)
else: else:
if self.field.type in ('id','integer'): if field.type in ('id', 'integer'):
if isinstance(value,(int,long)) or value.isdigit(): if isinstance(value, (int, long)) or value.isdigit():
value = int(value) value = int(value)
elif self.auto_add: elif self.auto_add:
value = self.maybe_add(table, self.fieldnames[0], value) value = self.maybe_add(table, self.fieldnames[0], value)
@@ -818,7 +818,7 @@ class IS_INT_IN_RANGE(Validator):
if regex_isint.match(str(value)): if regex_isint.match(str(value)):
v = int(value) v = int(value)
if ((self.minimum is None or v >= self.minimum) and if ((self.minimum is None or v >= self.minimum) and
(self.maximum is None or v < self.maximum)): (self.maximum is None or v < self.maximum)):
return (v, None) return (v, None)
return (value, self.error_message) return (value, self.error_message)
@@ -892,7 +892,7 @@ class IS_FLOAT_IN_RANGE(Validator):
else: else:
v = float(str(value).replace(self.dot, '.')) v = float(str(value).replace(self.dot, '.'))
if ((self.minimum is None or v >= self.minimum) and if ((self.minimum is None or v >= self.minimum) and
(self.maximum is None or v <= self.maximum)): (self.maximum is None or v <= self.maximum)):
return (v, None) return (v, None)
except (ValueError, TypeError): except (ValueError, TypeError):
pass pass
@@ -978,7 +978,7 @@ class IS_DECIMAL_IN_RANGE(Validator):
else: else:
v = decimal.Decimal(str(value).replace(self.dot, '.')) v = decimal.Decimal(str(value).replace(self.dot, '.'))
if ((self.minimum is None or v >= self.minimum) and if ((self.minimum is None or v >= self.minimum) and
(self.maximum is None or v <= self.maximum)): (self.maximum is None or v <= self.maximum)):
return (v, None) return (v, None)
except (ValueError, TypeError, decimal.InvalidOperation): except (ValueError, TypeError, decimal.InvalidOperation):
pass pass
@@ -2248,7 +2248,7 @@ class IS_DATE(Validator):
y = '%.4i' % year y = '%.4i' % year
format = format.replace('%y', y[-2:]) format = format.replace('%y', y[-2:])
format = format.replace('%Y', y) format = format.replace('%Y', y)
if year < 1900: if year < 1900:
year = 2000 year = 2000
d = datetime.date(year, value.month, value.day) d = datetime.date(year, value.month, value.day)
return d.strftime(format) return d.strftime(format)
@@ -2347,6 +2347,7 @@ class IS_DATE_IN_RANGE(IS_DATE):
(datetime.date(2010, 3, 3), 'oops') (datetime.date(2010, 3, 3), 'oops')
""" """
def __init__(self, def __init__(self,
minimum=None, minimum=None,
maximum=None, maximum=None,
@@ -2400,6 +2401,7 @@ class IS_DATETIME_IN_RANGE(IS_DATETIME):
(datetime.datetime(2010, 3, 3, 0, 0), 'oops') (datetime.datetime(2010, 3, 3, 0, 0), 'oops')
""" """
def __init__(self, def __init__(self,
minimum=None, minimum=None,
maximum=None, maximum=None,
@@ -2513,7 +2515,7 @@ def urlify(s, maxlen=80, keep_underscores=False):
if keep_underscores: if keep_underscores:
s = re.sub('\s+', '-', s) # whitespace to hyphens s = re.sub('\s+', '-', s) # whitespace to hyphens
s = re.sub('[^\w\-]', '', s) s = re.sub('[^\w\-]', '', s)
# strip all but alphanumeric/underscore/hyphen # strip all but alphanumeric/underscore/hyphen
else: else:
s = re.sub('[\s_]+', '-', s) # whitespace & underscores to hyphens s = re.sub('[\s_]+', '-', s) # whitespace & underscores to hyphens
s = re.sub('[^a-z0-9\-]', '', s) # strip all but alphanumeric/hyphen s = re.sub('[^a-z0-9\-]', '', s) # strip all but alphanumeric/hyphen
@@ -2705,6 +2707,7 @@ class LazyCrypt(object):
""" """
Stores a lazy password hash Stores a lazy password hash
""" """
def __init__(self, crypt, password): def __init__(self, crypt, password):
""" """
crypt is an instance of the CRYPT validator, crypt is an instance of the CRYPT validator,
@@ -2760,8 +2763,8 @@ class LazyCrypt(object):
# LazyCrypt objects comparison # LazyCrypt objects comparison
if isinstance(stored_password, self.__class__): if isinstance(stored_password, self.__class__):
return ((self is stored_password) or return ((self is stored_password) or
((self.crypt.key == stored_password.crypt.key) and ((self.crypt.key == stored_password.crypt.key) and
(self.password == stored_password.password))) (self.password == stored_password.password)))
if self.crypt.key: if self.crypt.key:
if ':' in self.crypt.key: if ':' in self.crypt.key:
@@ -3404,14 +3407,14 @@ class IS_IPV4(Validator):
ok = True ok = True
if not (self.is_localhost is None or self.is_localhost == if not (self.is_localhost is None or self.is_localhost ==
(number == self.localhost)): (number == self.localhost)):
ok = False ok = False
if not (self.is_private is None or self.is_private == if not (self.is_private is None or self.is_private ==
(sum([private_number[0] <= number <= private_number[1] (sum([private_number[0] <= number <= private_number[1]
for private_number in self.private]) > 0)): for private_number in self.private]) > 0)):
ok = False ok = False
if not (self.is_automatic is None or self.is_automatic == if not (self.is_automatic is None or self.is_automatic ==
(self.automatic[0] <= number <= self.automatic[1])): (self.automatic[0] <= number <= self.automatic[1])):
ok = False ok = False
if ok: if ok:
return (value, None) return (value, None)
return (value, translate(self.error_message)) return (value, translate(self.error_message))
@@ -3695,6 +3698,7 @@ class IS_IPADDRESS(Validator):
>>> IS_IPADDRESS(subnets='invalidsubnet')('2001::8ffa:fe22:b3af') >>> IS_IPADDRESS(subnets='invalidsubnet')('2001::8ffa:fe22:b3af')
('2001::8ffa:fe22:b3af', 'invalid subnet provided') ('2001::8ffa:fe22:b3af', 'invalid subnet provided')
""" """
def __init__( def __init__(
self, self,
minip='0.0.0.0', minip='0.0.0.0',
@@ -3757,7 +3761,7 @@ class IS_IPADDRESS(Validator):
is_private=self.is_private, is_private=self.is_private,
is_automatic=self.is_automatic, is_automatic=self.is_automatic,
error_message=self.error_message error_message=self.error_message
)(value) )(value)
elif self.is_ipv6 or isinstance(ip, IPv6Address): elif self.is_ipv6 or isinstance(ip, IPv6Address):
retval = IS_IPV6( retval = IS_IPV6(
is_private=self.is_private, is_private=self.is_private,
@@ -3769,7 +3773,7 @@ class IS_IPADDRESS(Validator):
is_teredo=self.is_teredo, is_teredo=self.is_teredo,
subnets=self.subnets, subnets=self.subnets,
error_message=self.error_message error_message=self.error_message
)(value) )(value)
else: else:
retval = (value, translate(self.error_message)) retval = (value, translate(self.error_message))