Skip to content
kk edited this page Jun 1, 2019 · 4 revisions

Schema and Model

from validr import T, modelclass

schema1 = T.dict(
    id=T.int.desc('The unique identifier for a product'),
    name=T.str.desc('Name of the product'),
    price=T.float.exmin(0),
    tags=T.list(
        T.str.minlen(1)
    ).unique
)

schema2 = T({
    "$self": "dict",
    "id": "int.desc('The unique identifier for a product')",
    "name": "str.desc('Name of the product')",
    "price": "float.exmin(0)",
    "tags": [
        "list.unique",
        "str.minlen(1)"
    ]
})

@modelclass
class Product:
    id=T.int.desc('The unique identifier for a product')
    name=T.str.desc('Name of the product')
    price=T.float.exmin(0)
    tags=T.list(
        T.str.minlen(1)
    ).unique

# they are equalment
assert schema1 == schema2 == Product
# the same content after convert to JSON string
assert str(schema1) == str(schema2) == str(Product.__schema__)
# is hashable,can be dict's key
assert hash(schema1) == hash(schema2) == hash(Product.__schema__)

Compiler

Schema should be compiled before validate data, there are 2 reasons:

  1. compile is slower than validate, and we only compile schema once, this can improve performance.
  2. the schema can be extented on compile time, eg: custom validators.
from validr import Compiler

compiler = Compiler()
validate = compiler.compile(schema)

Validate

>>> from validr import T, Compiler, Invalid
>>> f = Compiler().compile(T.int.min(0).max(9))
>>> f("3")
3
>>> f(-1)
...
validr._exception.Invalid: value must >= 0, value=-1
>>> f("abc")
...
validr._exception.Invalid: invalid int, value=abc

Convert to string or object:

>>> Compiler().compile(T.date)('2019-01-01')
'2019-01-01'
>>> Compiler().compile(T.date.object)('2019-01-01')
datetime.date(2019, 1, 1)
>>>

Handle Exception

Exceptions

ValidrError
  - Invalid
      - ModelInvalid
  - SchemaError

Handle Invalid:

f = Compiler().compile(
    T.dict(
        numbers=T.list(T.int)
    )
)
try:
    f({"numbers":[1,'x']})
except Invalid as ex:
    print(ex.message)
    print(ex.position)
    print(ex.field)
    print(ex.value)
    print(str(ex))
>>>
invalid int
numbers[1]
numbers
x
numbers[1]: invalid int, value=x
>>>

Replace invalid value with something:

>>> Compiler().compile(T.int.invalid_to(0))('x')
0
>>> Compiler().compile(T.int.default(0).invalid_to_default)('x')
0
>>> Compiler().compile(T.int.optional.invalid_to_default)('x')
None

Report error position:

mark_index and mark_key are used to add position infomations to ValidrError or it's subclass(eg: Invalid and SchemaError) object.

from validr import mark_index, mark_key, ValidrError

try:
    with mark_index(0):
        with mark_key('key'):
            with mark_index(-1):  # `-1` means the position is uncertainty
                raise ValidrError('message')
except ValidrError as ex:
    print(ex.position)  # [0].key[], the `[]` is corresponding to mark_index(-1)