Skip to content

Commit

Permalink
"Sometimes" validation and some other fixes (#28)
Browse files Browse the repository at this point in the history
Co-authored-by: Calina Cenan <[email protected]>
  • Loading branch information
calina-c and Calina Cenan authored Jan 21, 2021
1 parent f072fd8 commit 2c532a1
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 4 deletions.
4 changes: 4 additions & 0 deletions docs/source/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,10 @@ The given _field_ must match the field under validation.

The field under validation must have a size matching the given _value_. For string data, _value_ corresponds to the number of characters. For numeric data, _value_ corresponds to a given integer value. For an array, _size_ corresponds to the `count` of the array. For files, _size_ corresponds to the file size in kilobytes.

#### sometimes

The other validations will only apply if this field is present and non-empty. Incompatible with _required_ and _nullable_.

#### starts_with:_foo_,_bar_,...

The field under validation must start with one of the given values.
Expand Down
4 changes: 4 additions & 0 deletions flask_sieve/lang/en.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,25 +54,29 @@
'file': 'The :attribute must be less than :value_0 kilobytes.',
'string': 'The :attribute must be less than :value_0 characters.',
'array': 'The :attribute must have less than :value_0 items.',
'empty': 'The :attribute could not be validated since it is empty.'
},
'lte': {
'numeric': 'The :attribute must be less than or equal :value_0.',
'file': 'The :attribute must be less than or equal :value_0 kilobytes.',
'string': 'The :attribute must be less than or equal :value_0 characters.',
'array': 'The :attribute must not have more than :value_0 items.',
'empty': 'The :attribute could not be validated since it is empty.'
},
'max': {
'numeric': 'The :attribute may not be greater than :max_0.',
'file': 'The :attribute may not be greater than :max_0 kilobytes.',
'string': 'The :attribute may not be greater than :max_0 characters.',
'array': 'The :attribute may not have more than :max_0 items.',
'empty': 'The :attribute could not be validated since it is empty.'
},
'mime_types': 'The :attribute must be a file of type: :values_0.',
'min': {
'numeric': 'The :attribute must be at least :min_0.',
'file': 'The :attribute must be at least :min_0 kilobytes.',
'string': 'The :attribute must be at least :min_0 characters.',
'array': 'The :attribute must have at least :min_0 items.',
'empty': 'The :attribute could not be validated since it is empty.'
},
'not_in': 'The selected :attribute is invalid.',
'not_regex': 'The :attribute format is invalid.',
Expand Down
23 changes: 20 additions & 3 deletions flask_sieve/rules_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ def validate_extension(self, value, params, **kwargs):
def validate_file(value, **kwargs):
return isinstance(value, FileStorage)

@staticmethod
def validate_empty(value, **kwargs):
return value == ''

def validate_filled(self, value, attribute, nullable, **kwargs):
if self.validate_present(attribute):
return self.validate_required(value, attribute, nullable)
Expand Down Expand Up @@ -364,18 +368,24 @@ def validate_json(self, value, **kwargs):

def validate_lt(self, value, params, **kwargs):
self._assert_params_size(size=1, params=params, rule='lt')
if value == '':
return False
value = self._get_size(value)
lower = self._get_size(self._attribute_value(params[0]))
return value < lower

def validate_lte(self, value, params, **kwargs):
self._assert_params_size(size=1, params=params, rule='lte')
if value == '':
return False
value = self._get_size(value)
lower = self._get_size(self._attribute_value(params[0]))
return value <= lower

def validate_max(self, value, params, **kwargs):
self._assert_params_size(size=1, params=params, rule='max')
if value == '':
return False
value = self._get_size(value)
upper = self._get_size(params[0])
return value <= upper
Expand Down Expand Up @@ -406,6 +416,9 @@ def validate_not_regex(self, value, params, **kwargs):
def validate_nullable(value, **kwargs):
return True

def validate_sometimes(self, value, **kwargs):
return True

def validate_numeric(self, value, **kwargs):
return self._can_call_with_method(float, value)

Expand Down Expand Up @@ -527,14 +540,16 @@ def validate_uuid(value, **kwargs):
r'^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$',
str(value).lower()
) is not None

def _is_attribute_nullable(self, attribute, params, rules, **kwargs):
is_explicitly_nullable = self._has_rule(rules, 'nullable')
if is_explicitly_nullable:
return True
value = self._attribute_value(attribute)
if value is not None:
return False
is_optional = self._has_rule(rules, 'sometimes')
if is_optional and value is not None:
return True

attribute_conditional_rules = list(filter(lambda rule: rule['name'] in conditional_inclusion_rules, rules))
if len(attribute_conditional_rules) == 0:
return False
Expand Down Expand Up @@ -621,6 +636,8 @@ def _get_type_from_value(self, value):
return 'array'
elif self.validate_file(value):
return 'file'
elif self.validate_empty(value):
return 'empty'
return 'string'

def _has_any_of_rules(self, subset, rules):
Expand Down
47 changes: 46 additions & 1 deletion tests/test_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,17 @@ def validate_odd(value, **kwargs):
)
self.assertTrue(self._validator.passes())

def test_translates_validations_set_through_custom_handlers(self):
def validate_odd(value, **kwargs):
return int(value) % 2
self._validator.set_custom_handlers([
{
'handler': validate_odd,
'message':'This number must be odd.',
'params_count':0
}
])

def test_cannot_set_custom_handler_without_validate_keyword(self):
def method_odd(value, **kwargs):
return int(value) % 2
Expand All @@ -173,9 +184,43 @@ def method_odd(value, **kwargs):
params_count=0
)

def test_sometimes_request(self):
self.set_validator_params(
rules={'number': ['sometimes', 'max:5']},
request={}
)
self.assertTrue(self._validator.passes())

self.set_validator_params(
rules={'number': ['sometimes', 'max:5']},
request={'number': ''}
)
self.assertTrue(self._validator.fails())
self.assertDictEqual({
'number': [
'The number could not be validated since it is empty.'
]
}, self._validator.messages())

self.set_validator_params(
rules={'number': ['sometimes', 'max:5']},
request={'number': 2}
)
self.assertTrue(self._validator.passes())

self.set_validator_params(
rules={'number': ['sometimes', 'max:5']},
request={'number': 10}
)
self.assertTrue(self._validator.fails())
self.assertDictEqual({
'number': [
'The number may not be greater than 5.'
]
}, self._validator.messages())

def set_validator_params(self, rules=None, request=None):
rules = rules or {}
request = request or {}
self._validator.set_rules(rules)
self._validator.set_request(request)

0 comments on commit 2c532a1

Please sign in to comment.