# HG changeset patch # User Olly Cope # Date 1596468316 0 # Mon Aug 03 15:25:16 2020 +0000 # Node ID 2ad523b61fa8d9eb730b85d3dd8e842b45946e58 # Parent c1ae7e47ab12fe955200dffb72d209f4d1376db3 docs: add documentation for validation functions diff --git a/doc/index.rst b/doc/index.rst --- a/doc/index.rst +++ b/doc/index.rst @@ -10,6 +10,7 @@ :maxdepth: 2 rendering + validating form fields widgets diff --git a/doc/validating.rst b/doc/validating.rst new file mode 100644 --- /dev/null +++ b/doc/validating.rst @@ -0,0 +1,101 @@ +Validating and cleaning data +============================= + +Processors +---------- + +Before validation, any processing functions are run. These allow you do simple cleaning operations (eg calling `strip` on string values) before any validation rules are checked. +Processors can be configured by setting :attr:`~morf.fields.Field.processors`: + +.. code:: python + + class MyForm(Form): + + email = fields.Str(processors=[lambda s: s.lower().strip()]) + + +Validators +---------- + +Morf includes various common validator functions that can be +configured by setting :attr:`~morf.fields.Field.validators`: + +.. code:: python + + from morf import Form, fields, validators + + class MyForm(Form): + + email = fields.Str(validators=[validators.is_email()]) + message = fields.Str(validators=[validators.minlen(10)]) + + +Refer to :mod:`~morf.validators` for the list of built-in validation functions. + + +Ad-hoc validation functions +--------------------------- + +:meth:`~morf.form.validates` lets you create ad-hoc validation functions: + + +.. code:: python + + from morf import Form + from morf import validates + + class MyForm(Form): + + number1 = fields.Int() + number2 = fields.Int() + + @validates(number1) + def check_first_number_is_even(self, n1): + if n1 % 2 != 0: + self.fail("Even numbers only!") + + @validates(number1, number2) + def check_second_number_is_multiple_of_first(self, n1, n2): + if n2 % n1 != 0 + self.fail("Enter a multiple of the first number") + + @cleans(word) + def clean_word(self, word): + return word.lower() + + +:meth:`~morf.form.cleans` works in a similar way for data cleaning functions, +which are expected to return the cleaned data: + +.. code:: python + + from morf import Form + from morf import cleans + + class MyForm(Form): + + word = fields.Str() + + @cleans(word) + def clean_word(self, word): + return word.lower() + + +A validator or cleaner may also act on the whole form object: + +.. code:: python + + + @cleans + def clean_everything(self): + self.data['word'] = self.data['word'].lower() + + + +Validators reference +--------------------- + + +.. automodule:: morf.validators + :members: + diff --git a/morf/validators.py b/morf/validators.py --- a/morf/validators.py +++ b/morf/validators.py @@ -66,45 +66,75 @@ return True -def minlen(l, message=None): - return lambda v: assert_true(len(v) >= l, message) +def minlen(n, message=None): + """ + Check that ``len(value) ≥ n``. + """ + return lambda v: assert_true(len(v) >= n, message) -def maxlen(l, message=None): - return lambda v: assert_true(len(v) <= l, message) +def maxlen(n, message=None): + """ + Check that ``len(value) ≤ n``. + """ + return lambda v: assert_true(len(v) <= n, message) -def gt(l, message=None): - return lambda v: assert_true(v > l, message) +def gt(n, message=None): + """ + Check that the value is > ``n``. + """ + return lambda v: assert_true(v > n, message) -def gte(l, message=None): - return lambda v: assert_true(v >= l, message) +def gte(n, message=None): + """ + Check that the value is ≥ ``n``. + """ + return lambda v: assert_true(v >= n, message) -def lt(l, message=None): - return lambda v: assert_true(v < l, message) +def lt(n, message=None): + """ + Check that the value is < ``n``. + """ + return lambda v: assert_true(v < n, message) -def lte(l, message=None): - return lambda v: assert_true(v <= l, message) +def lte(n, message=None): + """ + Check that the value is ≤ ``n``. + """ + return lambda v: assert_true(v <= n, message) def matches(p, message=None): + """ + Check that the string value matches the given regular expression ``p``. + """ if isinstance(p, _strtypes): p = re.compile(p) return lambda v: assert_true(p.search(v) is not None, message) def notempty(message=None): + """ + Check that the value is a non-empty string + """ return matches(re.compile(r"\S"), message) def eq(expected, message=None): + """ + Check that the value equals ``expected``. + """ return lambda v: assert_true(v == expected, message) def is_in(allowed, message=None): + """ + Check that the value is contained in the list ``allowed``. + """ return lambda v: assert_true(v in allowed, message) @@ -161,9 +191,7 @@ digits = [int(x) for x in cc if x in string.digits] result = ( - sum( - digits[::-2] + [sum(divmod(d * 2, 10)) for d in digits[-2::-2]] - ) + sum(digits[::-2] + [sum(divmod(d * 2, 10)) for d in digits[-2::-2]]) % 10 ) return assert_true(result == 0, message)