# Validation

Redis OM uses [Pydantic][pydantic-url] behind the scenes to validate data at runtime, based on the model's type annotations.

## Basic Type Validation

Validation works for basic type annotations like `str`. Thus, given the following model:

```python
import datetime
from typing import Optional

from pydantic import EmailStr

from redis_om import HashModel


class Customer(HashModel):
    first_name: str
    last_name: str
    email: EmailStr
    join_date: datetime.date
    age: int
    bio: Optional[str]
```

... Redis OM will ensure that `first_name` is always a string.

But every Redis OM model is also a Pydantic model, so you can use existing Pydantic validators like `EmailStr`, `Pattern`, and many more for complex validation!

## Complex Validation

Let's see what happens if we try to create a `Customer` object with an invalid email address.

```python
import datetime
from typing import Optional

from pydantic import EmailStr, ValidationError

from redis_om import HashModel


class Customer(HashModel):
    first_name: str
    last_name: str
    email: EmailStr
    join_date: datetime.date
    age: int
    bio: Optional[str]


# We'll get a validation error if we try to use an invalid email address!
try:
    Customer(
        first_name="Andrew",
        last_name="Brookins",
        email="Not an email address!",
        join_date=datetime.date.today(),
        age=38,
        bio="Python developer, works at Redis, Inc."
    )
except ValidationError as e:
    print(e)
    """
    pydantic.error_wrappers.ValidationError: 1 validation error for Customer
     email
       value is not a valid email address (type=value_error.email)
    """
```

As you can see, creating the `Customer` object generated the following error:

```
 Traceback:
 pydantic.error_wrappers.ValidationError: 1 validation error for Customer
 email
   value is not a valid email address (type=value_error.email)
```

We'll also get a validation error if we change a field on a model instance to an invalid value and then try to save the model:

```python
import datetime
from typing import Optional

from pydantic import EmailStr, ValidationError

from redis_om import HashModel


class Customer(HashModel):
    first_name: str
    last_name: str
    email: EmailStr
    join_date: datetime.date
    age: int
    bio: Optional[str]


andrew = Customer(
    first_name="Andrew",
    last_name="Brookins",
    email="andrew.brookins@example.com",
    join_date=datetime.date.today(),
    age=38,
    bio="Python developer, works at Redis, Inc."
)

andrew.email = "Not valid"

try:
    andrew.save()
except ValidationError as e:
    print(e)
    """
    pydantic.error_wrappers.ValidationError: 1 validation error for Customer
     email
       value is not a valid email address (type=value_error.email)
    """
```

Once again, we get the validation error:

```
 Traceback:
 pydantic.error_wrappers.ValidationError: 1 validation error for Customer
 email
   value is not a valid email address (type=value_error.email)
```

## Constrained Values

If you want to use any of the constr

Pydantic includes many type annotations to introduce constraints to your model field values.

The concept of "constraints" includes quite a few possibilities:

* Strings that are always lowercase
* Strings that must match a regular expression
* Integers within a range
* Integers that are a specific multiple
* And many more...

All of these constraint types work with Redis OM models. Read the [Pydantic documentation on constrained types](https://pydantic-docs.helpmanual.io/usage/types/#constrained-types) to learn more.


[pydantic-url]: https://github.com/samuelcolvin/pydantic