diff --git a/README.md b/README.md index 96c7a3c..0459509 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,13 @@ span - [📇 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) + - [Querying](#querying) + - [Embedded Models](#embedded-models) - [💻 Installation](#-installation) - [📚 Documentation](#-documentation) -- [⛏️ Troubleshooting](#-troubleshooting) -- [✨ So, How Do You Get RediSearch and RedisJSON?](#-so-how-do-you-get-redisearch-and-redisjson) -- [❤️ Contributing](#-contributing) +- [⛏️ Troubleshooting](#️-troubleshooting) +- [✨ So How Do You Get RediSearch and RedisJSON?](#-so-how-do-you-get-redisearch-and-redisjson) +- [❤️ Contributing](#️-contributing) - [📝 License](#-license) @@ -199,7 +201,7 @@ from redis_om import ( ) from redis_om import get_redis_connection - + class Customer(HashModel): first_name: str last_name: str = Field(index=True) @@ -235,8 +237,6 @@ These queries -- and more! -- are possible because **Redis OM manages indexes fo Querying with this index features a rich expression syntax inspired by the Django ORM, SQLAlchemy, and Peewee. We think you'll enjoy it! -To learn more about how to query with Redis OM, see the [documentation on querying](docs/querying.md). -**** ### 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. @@ -292,8 +292,6 @@ Customer.find(Customer.address.city == "San Antonio", Customer.address.state == "TX") ``` -To learn more, read the [documentation on embedded models](docs/embedded.md). - ## 💻 Installation Installation is simple with `pip`, Poetry, or Pipenv. @@ -314,8 +312,7 @@ The Redis OM documentation is available [here](docs/index.md). If you run into trouble or have any questions, we're here to help! -First, check the [FAQ](docs/faq.md). If you don't find the answer there, -hit us up on the [Redis Discord Server](http://discord.gg/redis). +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). ## ✨ So How Do You Get RediSearch and RedisJSON? diff --git a/docs/connections.md b/docs/connections.md deleted file mode 100644 index 10eff89..0000000 --- a/docs/connections.md +++ /dev/null @@ -1,3 +0,0 @@ -# Managing Connections - -WIP! \ No newline at end of file diff --git a/docs/embedded_models.md b/docs/embedded_models.md deleted file mode 100644 index d08b7ba..0000000 --- a/docs/embedded_models.md +++ /dev/null @@ -1,54 +0,0 @@ -# Embedded Models - -**NOTE:** This documentation is a stub, using the same embedded JSON model example as the README. - -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. - -```python -import datetime -from typing import Optional - -from redis_om import ( - EmbeddedJsonModel, - JsonModel, - Field, - Migrator -) - - -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 - - -# With these two models and a Redis deployment with the RedisJSON -# module installed, we can run queries like the following. - -# 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! -Migrator().run() - -# Find all customers who live in San Antonio, TX -Customer.find(Customer.address.city == "San Antonio", - Customer.address.state == "TX") -``` \ No newline at end of file diff --git a/docs/faq.md b/docs/faq.md deleted file mode 100644 index 78203b2..0000000 --- a/docs/faq.md +++ /dev/null @@ -1,3 +0,0 @@ -# Frequently Asked Questions (FAQ) - -WIP! \ No newline at end of file diff --git a/docs/fastapi_integration.md b/docs/fastapi_integration.md index 5a8d771..423263e 100644 --- a/docs/fastapi_integration.md +++ b/docs/fastapi_integration.md @@ -2,9 +2,9 @@ ## Introduction -This section includes a complete example showing how to integrate Redis OM with FastAPI. +Good news: Redis OM was specifically designed to integrate with FastAPI! -Good news: Redis OM was **specifically designed to integrate with FastAPI**! +This section includes a complete example showing how to integrate Redis OM with FastAPI. ## Concepts @@ -131,4 +131,89 @@ Get a copy of the value for "pk" and make another request to get that customer: You can also get a list of all customer PKs: $ curl "http://localhost:8000/customers" - {"customers":["01FM2G8EP38AVMH7PMTAJ123TA"]} \ No newline at end of file + {"customers":["01FM2G8EP38AVMH7PMTAJ123TA"]} + +## Redsi OM with Asyncio + +Redis OM is designed to work with asyncio, so you can use Redis OM models asynchronously within FastAPI applications. + +The only difference is that you import the Redis OM models from the `aredis_om` module instead of the `redis_om` module. + +Here is the previous FastAPI app, but using asyncio-compatible Redis OM code: + +```python +import datetime +from typing import Optional + +import aioredis + +from fastapi import FastAPI, HTTPException +from starlette.requests import Request +from starlette.responses import Response + +from fastapi_cache import FastAPICache +from fastapi_cache.backends.redis import RedisBackend +from fastapi_cache.decorator import cache + +from pydantic import EmailStr + +from aredis_om import HashModel, NotFoundError # <- Notice, we import from aredis_om +from aredis_om import get_redis_connection + +# This Redis instance is tuned for durability. +REDIS_DATA_URL = "redis://localhost:6380" + +# This Redis instance is tuned for cache performance. +REDIS_CACHE_URL = "redis://localhost:6381" + + +class Customer(HashModel): + first_name: str + last_name: str + email: EmailStr + join_date: datetime.date + age: int + bio: Optional[str] + + +app = FastAPI() + + +@app.post("/customer") +async def save_customer(customer: Customer): + # We can save the model to Redis by calling `save()`: + return await customer.save() # <- We use await here + + +@app.get("/customers") +async def list_customers(request: Request, response: Response): + # To retrieve this customer with its primary key, we use `Customer.get()`: + return {"customers": await Customer.all_pks()} # <- We also use await here + + +@app.get("/customer/{pk}") +@cache(expire=10) +async def get_customer(pk: str, request: Request, response: Response): + # To retrieve this customer with its primary key, we use `Customer.get()`: + try: + return await Customer.get(pk) # <- And, finally, one more await! + except NotFoundError: + raise HTTPException(status_code=404, detail="Customer not found") + + +@app.on_event("startup") +async def startup(): + r = aioredis.from_url(REDIS_CACHE_URL, encoding="utf8", + decode_responses=True) + FastAPICache.init(RedisBackend(r), prefix="fastapi-cache") + + # You can set the Redis OM URL using the REDIS_OM_URL environment + # variable, or by manually creating the connection using your model's + # Meta object. + Customer.Meta.database = get_redis_connection(url=REDIS_DATA_URL, + decode_responses=True) +``` + +**NOTE:** The modules `redis_om` and `aredis_om` are identical in almost every +way. The only difference is that the `aredis_om` returns coroutines that you must +`await`. diff --git a/docs/integrating.md b/docs/integrating.md deleted file mode 100644 index 404c079..0000000 --- a/docs/integrating.md +++ /dev/null @@ -1,3 +0,0 @@ -# Integration Redis OM With Popular Frameworks - -WIP! \ No newline at end of file diff --git a/docs/models_and_fields.md b/docs/models_and_fields.md deleted file mode 100644 index 0c082ba..0000000 --- a/docs/models_and_fields.md +++ /dev/null @@ -1,31 +0,0 @@ -# Models and Fields - -**NOTE:** This documentation is a stub. Documentation for this project is a work in progress! - -## Introduction - -## Saving Data As Hashes With HashModel - -### What Does Redis Store? - -## Saving Data With JSON With JsonModel - -### What Does Redis Store? - -## Primary Keys - -### Why Primary Keys Matter to Redis OM - -### Using the Default Primary Key - -### Using a Custom Primary Key - -## Meta Classes - -## Subclassing Models - -### Subclassing and Meta Objects - -## Saving Models - - diff --git a/docs/querying.md b/docs/querying.md deleted file mode 100644 index 2a11e65..0000000 --- a/docs/querying.md +++ /dev/null @@ -1,67 +0,0 @@ -# Querying - -**NOTE:** This documentation is a stub that uses examples from other documentation in this project (the README, the Getting Started guide, etc.). Detailed documentation on querying in a work in progress. - -Querying in Redis OM uses a rich expression syntax inspired by the Django ORM, SQLAlchemy, and Peewee. - -In the following example, we define `Address` and `Customer` models for use with a Redis database that has the [RedisJSON](redis-json-url) module installed. - -With these two classes defined, we can query on any indexed fields in the models -- including indexed fields within embedded models. - -```python -import datetime -from typing import Optional - -from redis_om import ( - EmbeddedJsonModel, - JsonModel, - Field, - Migrator -) - - -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 - - -# 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! -Migrator().run() - -# Here are a few example queries that use these two models... - -# Find all customers with the last name "Brookins" -Customer.find(Customer.last_name == "Brookins").all() - -# Find all customers that do NOT have the last name "Brookins" -Customer.find(Customer.last_name != "Brookins").all() - -# 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") | ( - Customer.age == 100 -) & (Customer.last_name == "Smith")).all() - -# Find all customers who live in San Antonio, TX -Customer.find(Customer.address.city == "San Antonio", - Customer.address.state == "TX") -``` \ No newline at end of file diff --git a/docs/testing.md b/docs/testing.md deleted file mode 100644 index 7cb7055..0000000 --- a/docs/testing.md +++ /dev/null @@ -1,5 +0,0 @@ -# Testing Your Models - -**NOTE:** This documentation is a Work in Progress. - -Writing tests that use a Redis OM model requires some setup. For now, review the tests in the redis-om-python project for examples.