# Redis OM Python: Pydantic models that live in Redis

> Redis OM Python is an MIT licensed library that lets you declare a Pydantic model, save it to Redis as a hash, and query it with a fluent expression language, with the query layer relying on RediSearch and RedisJSON. It suits a Python service that wants schema validation and secondary indexes without writing a document mapper, and it is a poor fit if your Redis deployment lacks the modules the query layer needs.

**redis/redis-om-python** — Object mapping, and more, for Redis and Python

- Repository: https://github.com/redis/redis-om-python
- Stars: 1,319 · Forks: 131
- Language: Python
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/redis-redis-om-python

## One class gives you validation, storage and an index

Redis OM Python is described as making it easy to model Redis data in Python applications, and the feature list for this preview release is three items: declarative object mapping for Redis objects, declarative secondary-index generation, and fluent APIs for querying Redis. The model is a Pydantic subclass, and that is where the validation comes from, since the library uses Pydantic to validate data against the type annotations on the fields. The point of the design is that you declare the shape once and get three things from it, a validator, a serializer and a set of indexes, rather than maintaining those separately. The library is one of a family, with the README linking Redis OM for .NET, Node.js and Spring alongside the Python version, so the API shape is recognisable if you have used another one.

## The model class is a Pydantic model with a save method

The customer example in the README shows the whole surface. You declare a HashModel subclass with typed fields, including an optional one with a default, and you get a primary key generated for you.

```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] = None


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."
)

print(andrew.pk)
# > "01FJM6PH661HCNNRC884H6K30C"

andrew.save()
```

The primary key is a ULID generated without talking to Redis, which is what the ulid-style value in the output shows. Saving is a method on the instance, and the README also shows an expire call to set a TTL in seconds on the stored hash, and a get that asserts a round trip back to an equal model.

## Validation is Pydantic's, so it is the validators you already know

The README is explicit that every Redis OM model is also a Pydantic model, so the whole validator library applies as a drop-in type annotation, mentioning EmailStr, Pattern and many more, and that you can write arbitrarily complex custom validations. The example catches a bad email address.

```python
from pydantic import EmailStr, ValidationError

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)
```

The important detail is where the check happens. Because validation is part of the model definition, an invalid object cannot be constructed, which means it cannot be saved either. The library does not need a separate write-time validation path, and the error you get is a Pydantic error with the field name in it rather than a Redis type error.

## Rich queries need the modules, plain saving does not

The README draws a line that determines what you can use. Data modelling, validation and saving models to Redis work regardless of how you run Redis, but the rich query expressions and embedded models are available when the RediSearch and RedisJSON modules are installed in your deployment, or when you are using Redis Enterprise. The query language is expression-based, so you write a Python expression rather than a query string, and the model can be extended with index fields. The README includes a section explaining what a Redis module is and how to get RediSearch and RedisJSON, and a troubleshooting section, which is where the difference between a working model and a working query usually gets resolved. The embedded model part is the other side of the same requirement, since a document that needs a JSON view is not the same thing as a flat hash.

## Redis Stack is the quick way to get a working instance

Before writing any code you need a Redis instance with the right modules, and the README's answer is a single Docker command that launches redis-stack, an extension of Redis that adds modern data structures, with the redis-insight GUI available on a second port.

```sh
docker run -p 6379:6379 -p 8001:8001 redis/redis-stack
```

The compose file in the repository root is the same idea with two instances, a redis/redis-stack:latest on port 6380 and a plain redis:latest on port 6381, which is a good way to see the difference between a deployment with modules and one without. The Makefile points its default Redis URL at redis://localhost:6380?decode_responses=True, and the test targets reflect the same split, with make test running against redislabs/redisearch:edge and make test_oss running against redis:latest. Installation itself is one command with pip, or uv add, and the package is published as redis-om.

## The build is unusual, because one directory is generated from another

The repository layout explains why the Makefile has a sync target. There is an aredis_om/ directory holding the source, a redis_om/ directory that is the published module, and a make_sync.py script that generates one from the other, along with a tests_sync directory. The Makefile's sync target runs that script, and lint, format and test all depend on it, so the generated directory is never edited by hand. The dist target builds the package after a clean and a sync, and the clean target removes the generated redis_om and tests_sync directories along with the build and dist output. Linting is ruff for style and mypy for types, both run through uv, and the package also installs a console script named om.

## A 1.x version number on a library that calls itself a preview

The metadata and the README disagree slightly about maturity, and the disagreement is worth noting. The pyproject.toml classifies the project as Development Status 5, Production/Stable, gives the version as 1.2.0 and the licence as BSD-3-Clause, and the release history matches with v1.2.0 on 2026-10-01, v1.1.0 in March 2026 and v1.0.6 in January 2026. The README, however, calls this a preview release and lists its three features as what the preview contains. The dependency pins are also unusually specific, with redis constrained to be at least 6.3.0, not 8.0.0 and below 9.0.0, and the comment in pyproject.toml explains why, since redis-py 8.0.0 returns empty RESP3 search results and RedisVL 0.27 or newer is the first release accepting redis-py 8.1 and up. That is the kind of note that only exists because someone hit the bug.

## Conclusion

Redis OM Python fits a Python service that wants one schema definition to drive validation, persistence and querying, since the model class is a Pydantic model and the index generation is declarative rather than something you maintain by hand. It is a poor fit if you are running plain Redis without the RediSearch and RedisJSON modules, because the rich query and embedded model features are unavailable there even though the basic model and save path still works, and it is a poor fit if you need a mature production ORM. Verify first that your deployment can load the modules, since the README dev container runs redis/redis-stack and the compose file runs both a stack image and a plain redis image side by side, and note that the library is still described as a preview release despite a 1.x version number.

## FAQ

### What does Redis OM Python actually give me?

The README lists three features in this preview release: declarative object mapping for Redis objects, declarative secondary-index generation, and fluent APIs for querying Redis. A model is a Pydantic subclass, so validation, serialization and persistence all come from the type annotations.

### Do I need RediSearch and RedisJSON to use Redis OM Python?

Not for the basics. The README says data modelling, validation and saving models to Redis work regardless of how you run Redis. The rich query expressions and embedded models require the RediSearch and RedisJSON modules, or Redis Enterprise.

### How do I install Redis OM Python and a test server?

Install the package with pip install redis-om, or uv add redis-om, and start a server with docker run -p 6379:6379 -p 8001:8001 redis/redis-stack, which also gives you the redis-insight GUI on port 8001.

### Is Redis OM Python production ready?

The README calls this a preview release and lists the three features it contains, while the package metadata declares Development Status 5, Production/Stable and version 1.2.0. The dependency on Pydantic 2, the redis-py version exclusion of 8.0.0 and the redisvl requirement of 0.27 or newer are all pinned tightly in pyproject.toml.

## Sources

- [Issues](https://github.com/redis/redis-om-python/issues)
- [License: MIT](https://github.com/redis/redis-om-python/blob/main/LICENSE)
- [README](https://github.com/redis/redis-om-python/blob/main/README.md)
- [redis/redis-om-python on GitHub](https://github.com/redis/redis-om-python)
- [Releases](https://github.com/redis/redis-om-python/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/redis-redis-om-python
