2021-11-02 22:22:32 +01:00
< div align = "center" >
< br / >
< br / >
2021-11-10 21:09:51 +01:00
< img width = "360" src = "https://raw.githubusercontent.com/redis-developer/redis-om-python/main/images/logo.svg?token=AAAXXHUYL6RHPESRRAMBJOLBSVQXE" alt = "Redis OM" / >
2021-11-02 22:22:32 +01:00
< br / >
< br / >
< / div >
2021-10-20 00:06:50 +02:00
< p align = "center" >
< p align = "center" >
2021-11-02 22:23:02 +01:00
Object mapping, and more, for Redis and Python
2021-10-20 00:06:50 +02:00
< / p >
< / p >
2021-08-31 03:08:07 +02:00
2021-10-20 00:06:50 +02:00
---
2021-08-31 03:08:07 +02:00
2021-10-20 00:06:50 +02:00
[![Version][version-svg]][package-url]
[![License][license-image]][license-url]
[![Build Status][ci-svg]][ci-url]
2021-08-31 03:08:07 +02:00
2021-10-23 02:05:10 +02:00
**Redis OM Python** makes it easy to model Redis data in your Python applications.
2021-08-31 03:08:07 +02:00
2022-01-18 07:53:38 +01:00
[Redis OM .NET ](https://github.com/redis/redis-om-dotnet ) | [Redis OM Node.js ](https://github.com/redis/redis-om-node ) | [Redis OM Spring ](https://github.com/redis/redis-om-spring ) | **Redis OM Python**
2021-08-31 03:08:07 +02:00
2021-10-20 00:06:50 +02:00
< details >
< summary > < strong > Table of contents< / strong > < / summary >
2021-11-02 22:17:49 +01:00
span
2021-10-20 00:06:50 +02:00
<!-- DON'T EDIT THIS SECTION, INSTEAD RE - RUN doctoc TO UPDATE -->
2021-11-02 22:17:49 +01:00
- [💡 Why Redis OM? ](#-why-redis-om )
- [📇 Modeling Your Data ](#-modeling-your-data )
- [✓ Validating Data With Your Model ](#-validating-data-with-your-model )
- [🔎 Rich Queries and Embedded Models ](#-rich-queries-and-embedded-models )
2021-11-13 02:38:26 +01:00
- [Querying ](#querying )
- [Embedded Models ](#embedded-models )
2022-03-29 21:23:55 +02:00
- [Calling Other Redis Commands ](#calling-other-redis-commands )
2021-11-02 22:17:49 +01:00
- [💻 Installation ](#-installation )
- [📚 Documentation ](#-documentation )
2021-11-13 02:38:26 +01:00
- [⛏️ Troubleshooting ](#️ -troubleshooting )
- [✨ So How Do You Get RediSearch and RedisJSON? ](#-so-how-do-you-get-redisearch-and-redisjson )
- [❤️ Contributing ](#️ -contributing )
2021-11-02 22:17:49 +01:00
- [📝 License ](#-license )
2021-10-20 00:06:50 +02:00
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
< / details >
2021-10-23 02:05:10 +02:00
## 💡 Why Redis OM?
2021-10-25 23:43:48 +02:00
Redis OM provides high-level abstractions that make it easy to model and query data in Redis with modern Python applications.
2021-10-20 00:06:50 +02:00
2021-10-23 02:05:10 +02:00
This **preview** release contains the following features:
2021-10-20 00:06:50 +02:00
2021-10-23 02:05:10 +02:00
* Declarative object mapping for Redis objects
* Declarative secondary-index generation
* Fluent APIs for querying Redis
2021-10-20 00:06:50 +02:00
2021-10-25 23:43:48 +02:00
## 📇 Modeling Your Data
2021-10-20 00:06:50 +02:00
2021-10-25 23:43:48 +02:00
Redis OM contains powerful declarative models that give you data validation, serialization, and persistence to Redis.
2021-10-20 00:06:50 +02:00
2021-10-25 23:43:48 +02:00
Check out this example of modeling customer data with Redis OM. First, we create a `Customer` model:
2021-10-22 17:31:08 +02:00
```python
import datetime
from typing import Optional
from pydantic import EmailStr
2021-11-10 00:59:10 +01:00
from redis_om import HashModel
2021-10-22 17:31:08 +02:00
class Customer(HashModel):
first_name: str
last_name: str
email: EmailStr
join_date: datetime.date
age: int
bio: Optional[str]
```
2021-10-23 02:05:10 +02:00
Now that we have a `Customer` model, let's use it to save customer data to Redis.
2021-10-22 17:31:08 +02:00
```python
2021-11-02 22:17:49 +01:00
import datetime
from typing import Optional
from pydantic import EmailStr
2021-11-10 00:59:10 +01:00
from redis_om import HashModel
2021-11-02 22:17:49 +01:00
class Customer(HashModel):
first_name: str
last_name: str
email: EmailStr
join_date: datetime.date
age: int
bio: Optional[str]
# First, we create a new `Customer` object:
2021-10-22 17:31:08 +02:00
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."
)
2021-11-02 22:17:49 +01:00
# The model generates a globally unique primary key automatically
# without needing to talk to Redis.
2021-10-22 17:31:08 +02:00
print(andrew.pk)
2021-11-25 03:12:27 +01:00
# > "01FJM6PH661HCNNRC884H6K30C"
2021-10-22 17:31:08 +02:00
2021-11-02 22:17:49 +01:00
# We can save the model to Redis by calling `save()`:
2021-10-22 17:31:08 +02:00
andrew.save()
2021-10-23 02:05:10 +02:00
2022-03-29 23:19:08 +02:00
# Expire the model after 2 mins (120 seconds)
andrew.expire(120)
2021-11-02 22:17:49 +01:00
# To retrieve this customer with its primary key, we use `Customer.get()`:
assert Customer.get(andrew.pk) == andrew
2021-10-23 02:05:10 +02:00
```
2021-10-20 00:06:50 +02:00
2021-10-25 23:21:39 +02:00
**Ready to learn more?** Check out the [getting started ](docs/getting_started.md ) guide.
2021-10-20 00:06:50 +02:00
2021-10-25 23:21:39 +02:00
Or, continue reading to see how Redis OM makes data validation a snap.
2021-10-20 00:06:50 +02:00
2021-10-25 23:43:48 +02:00
## ✓ Validating Data With Your Model
2021-10-20 00:06:50 +02:00
2021-10-30 02:31:36 +02:00
Redis OM uses [Pydantic][pydantic-url] to validate data based on the type annotations you assign to fields in a model class.
2021-10-20 00:06:50 +02:00
2021-10-25 23:43:48 +02:00
This validation ensures that fields like `first_name` , which the `Customer` model marked as a `str` , are always strings. **But every Redis OM model is also a Pydantic model** , so you can use Pydantic validators like `EmailStr` , `Pattern` , and many more for complex validations!
2021-10-20 01:51:55 +02:00
2021-10-25 23:45:34 +02:00
For example, because we used the `EmailStr` type for the `email` field, we'll get a validation error if we try to create a `Customer` with an invalid email address:
2021-10-20 00:06:50 +02:00
```python
import datetime
from typing import Optional
2021-11-02 22:17:49 +01:00
from pydantic import EmailStr, ValidationError
2021-10-22 17:31:08 +02:00
2021-11-10 00:59:10 +01:00
from redis_om import HashModel
2021-11-02 22:17:49 +01:00
class Customer(HashModel):
first_name: str
last_name: str
email: EmailStr
join_date: datetime.date
age: int
bio: Optional[str]
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)
"""
2021-10-25 23:21:39 +02:00
```
2021-10-25 23:43:48 +02:00
**Any existing Pydantic validator should work** as a drop-in type annotation with a Redis OM model. You can also write arbitrarily complex custom validations!
2021-10-25 23:21:39 +02:00
To learn more, see the [documentation on data validation ](docs/validation.md ).
2021-10-22 17:31:08 +02:00
2021-10-25 23:43:48 +02:00
## 🔎 Rich Queries and Embedded Models
2021-10-22 17:31:08 +02:00
2021-10-25 23:21:39 +02:00
Data modeling, validation, and saving models to Redis all work regardless of how you run Redis.
2021-10-30 02:31:36 +02:00
Next, we'll show you the **rich query expressions** and **embedded models** Redis OM provides when the [RediSearch][redisearch-url] and [RedisJSON][redis-json-url] modules are installed in your Redis deployment, or you're using [Redis Enterprise][redis-enterprise-url].
2021-10-25 23:21:39 +02:00
2021-10-25 23:48:02 +02:00
**TIP**: *Wait, what's a Redis module?* If you aren't familiar with Redis modules, review the [So, How Do You Get RediSearch and RedisJSON? ](#-so-how-do-you-get-redisearch-and-redisjson ) section of this README.
2021-10-22 17:31:08 +02:00
### Querying
2021-10-30 02:31:36 +02:00
Redis OM comes with a rich query language that allows you to query Redis with Python expressions.
To show how this works, we'll make a small change to the `Customer` model we defined earlier. We'll add `Field(index=True)` to tell Redis OM that we want to index the `last_name` and `age` fields:
2021-10-25 23:21:39 +02:00
```python
2021-11-02 22:17:49 +01:00
import datetime
from typing import Optional
from pydantic import EmailStr
2021-11-10 00:59:10 +01:00
from redis_om import (
2021-11-02 22:17:49 +01:00
Field,
HashModel,
Migrator
)
2021-11-13 02:38:26 +01:00
2021-10-25 23:21:39 +02:00
class Customer(HashModel):
first_name: str
last_name: str = Field(index=True)
email: EmailStr
join_date: datetime.date
age: int = Field(index=True)
bio: Optional[str]
2021-11-10 00:59:10 +01:00
2021-11-02 22:17:49 +01:00
# Now, if we use this model with a Redis deployment that has the
# RediSearch module installed, we can run queries like the following.
2021-10-25 23:21:39 +02:00
2021-11-02 22:17:49 +01:00
# Before running queries, we need to run migrations to set up the
# indexes that Redis OM will use. You can also use the `migrate`
# CLI tool for this!
2021-11-25 03:12:27 +01:00
Migrator().run()
2021-10-25 23:21:39 +02:00
# Find all customers with the last name "Brookins"
Customer.find(Customer.last_name == "Brookins").all()
2021-10-22 17:31:08 +02:00
2021-10-25 23:21:39 +02:00
# Find all customers that do NOT have the last name "Brookins"
Customer.find(Customer.last_name != "Brookins").all()
2021-11-10 00:59:10 +01:00
2021-10-25 23:21:39 +02:00
# Find all customers whose last name is "Brookins" OR whose age is
# 100 AND whose last name is "Smith"
Customer.find((Customer.last_name == "Brookins") | (
2021-11-10 00:59:10 +01:00
Customer.age == 100
2021-10-25 23:21:39 +02:00
) & (Customer.last_name == "Smith")).all()
```
2021-10-22 17:31:08 +02:00
2021-10-25 23:21:39 +02:00
These queries -- and more! -- are possible because **Redis OM manages indexes for you automatically** .
2021-10-20 01:51:55 +02:00
2022-04-07 11:33:53 +02:00
Querying with this index features a rich expression syntax inspired by the Django ORM, SQLAlchemy, and Peewee. We think you'll enjoy it!
**Note:** Indexing only works for data stored in Redis logical database 0. If you are using a different database number when connecting to Redis, you can expect the code to raise a `MigrationError` when you run the migrator.
2021-10-25 23:21:39 +02:00
### Embedded Models
Redis OM can store and query **nested models** like any document database, with the speed and power you get from Redis. Let's see how this works.
In the next example, we'll define a new `Address` model and embed it within the `Customer` model.
2021-10-20 00:06:50 +02:00
```python
import datetime
from typing import Optional
2021-11-10 00:59:10 +01:00
from redis_om import (
2021-10-20 00:06:50 +02:00
EmbeddedJsonModel,
JsonModel,
Field,
2021-11-10 00:59:10 +01:00
Migrator,
2021-10-20 00:06:50 +02:00
)
2021-11-10 00:59:10 +01:00
2021-10-20 00:06:50 +02:00
class Address(EmbeddedJsonModel):
address_line_1: str
address_line_2: Optional[str]
city: str = Field(index=True)
state: str = Field(index=True)
country: str
postal_code: str = Field(index=True)
class Customer(JsonModel):
first_name: str = Field(index=True)
last_name: str = Field(index=True)
email: str = Field(index=True)
join_date: datetime.date
age: int = Field(index=True)
bio: Optional[str] = Field(index=True, full_text_search=True,
default="")
# Creates an embedded model.
address: Address
2021-11-02 22:17:49 +01:00
# With these two models and a Redis deployment with the RedisJSON
# module installed, we can run queries like the following.
2021-10-20 00:06:50 +02:00
2021-11-02 22:17:49 +01:00
# Before running queries, we need to run migrations to set up the
# indexes that Redis OM will use. You can also use the `migrate`
# CLI tool for this!
2021-11-25 03:12:27 +01:00
Migrator().run()
2021-10-20 00:06:50 +02:00
# Find all customers who live in San Antonio, TX
Customer.find(Customer.address.city == "San Antonio",
Customer.address.state == "TX")
```
2022-03-29 21:21:54 +02:00
## Calling Other Redis Commands
Sometimes you'll need to run a Redis command directly. Redis OM supports this through the `db` method on your model's class. This returns a connected Redis client instance which exposes a function named for each Redis command. For example, let's perform some basic set operations:
```python
from redis_om import HashModel
class Demo(HashModel):
some_field: str
redis_conn = Demo.db()
redis_conn.sadd("myset", "a", "b", "c", "d")
# Prints False
print(redis_conn.sismember("myset", "e"))
# Prints True
print(redis_conn.sismember("myset", "b"))
```
The parameters expected by each command function are those documented on the command's page on [redis.io ](https://redis.io/commands/ ).
If you don't want to get a Redis connection from a model class, you can also use `get_redis_connection` :
```python
from redis_om import get_redis_connection
redis_conn = get_redis_conection()
redis_conn.set("hello", "world")
```
2021-10-20 00:06:50 +02:00
## 💻 Installation
Installation is simple with `pip` , Poetry, or Pipenv.
```sh
2021-10-20 01:53:19 +02:00
# With pip
2021-10-20 01:50:34 +02:00
$ pip install redis-om
2021-10-20 00:06:50 +02:00
# Or, using Poetry
2021-10-20 01:50:34 +02:00
$ poetry add redis-om
2021-10-20 00:06:50 +02:00
```
## 📚 Documentation
2021-10-25 23:21:39 +02:00
The Redis OM documentation is available [here ](docs/index.md ).
2021-10-20 00:06:50 +02:00
## ⛏️ Troubleshooting
2021-11-02 22:17:49 +01:00
If you run into trouble or have any questions, we're here to help!
2021-10-20 00:06:50 +02:00
2021-11-13 02:38:26 +01:00
Hit us up on the [Redis Discord Server ](http://discord.gg/redis ) or [open an issue on GitHub ](https://github.com/redis-developer/redis-om-python/issues/new ).
2021-10-20 00:06:50 +02:00
2021-10-25 23:43:48 +02:00
## ✨ So How Do You Get RediSearch and RedisJSON?
2021-10-20 01:57:13 +02:00
2021-10-30 02:31:36 +02:00
Some advanced features of Redis OM rely on core features from two source available Redis modules: [RediSearch][redisearch-url] and [RedisJSON][redis-json-url].
2021-10-20 00:11:58 +02:00
2021-10-30 02:31:36 +02:00
You can run these modules in your self-hosted Redis deployment, or you can use [Redis Enterprise][redis-enterprise-url], which includes both modules.
2021-10-20 00:11:58 +02:00
2021-10-22 17:31:08 +02:00
To learn more, read [our documentation ](docs/redis_modules.md ).
2021-10-20 00:06:50 +02:00
## ❤️ Contributing
We'd love your contributions!
2021-11-18 22:28:46 +01:00
**Bug reports** are especially helpful at this stage of the project. [You can open a bug report on GitHub ](https://github.com/redis/redis-om-python/issues/new ).
2021-10-20 00:06:50 +02:00
2021-11-18 22:28:46 +01:00
You can also **contribute documentation** -- or just let us know if something needs more detail. [Open an issue on GitHub ](https://github.com/redis/redis-om-python/issues/new ) to get started.
2021-10-20 00:06:50 +02:00
2021-10-25 23:49:24 +02:00
## 📝 License
2021-10-20 00:06:50 +02:00
2022-03-29 21:34:30 +02:00
Redis OM uses the [MIT license][license-url].
2021-10-20 00:06:50 +02:00
<!-- Badges -->
2021-10-20 01:50:34 +02:00
[version-svg]: https://img.shields.io/pypi/v/redis-om?style=flat-square
[package-url]: https://pypi.org/project/redis-om/
2021-11-23 20:57:47 +01:00
[ci-svg]: https://img.shields.io/github/workflow/status/redis/redis-om-python/CI?style=flat-square
[ci-url]: https://github.com/redis/redis-om-python/actions/workflows/CI.yml
2022-03-29 21:34:30 +02:00
[license-image]: https://img.shields.io/badge/license-mit-green.svg?style=flat-square
2021-10-20 00:06:50 +02:00
[license-url]: LICENSE
<!-- Links -->
2021-10-22 15:33:05 +02:00
[redis-om-website]: https://developer.redis.com
[redis-om-js]: https://github.com/redis-om/redis-om-js
[redis-om-dotnet]: https://github.com/redis-om/redis-om-dotnet
[redis-om-spring]: https://github.com/redis-om/redis-om-spring
2021-10-20 00:06:50 +02:00
[redisearch-url]: https://oss.redis.com/redisearch/
[redis-json-url]: https://oss.redis.com/redisjson/
[pydantic-url]: https://github.com/samuelcolvin/pydantic
[ulid-url]: https://github.com/ulid/spec
2021-10-25 23:43:48 +02:00
[redis-enterprise-url]: https://redis.com/try-free/