### Run Redis Stack with Docker Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/installation.md Starts a Redis Stack server instance using Docker, which includes the JSON module by default. This is a recommended way to set up Redis for Rapyer. ```bash docker run -d --name redis-stack -p 6379:6379 redis/redis-stack-server:latest ``` -------------------------------- ### Install Redis Stack on macOS Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/installation.md Installs the Redis Stack server using Homebrew on macOS. This is a convenient way to set up Redis for development on macOS. ```bash brew install redis-stack ``` -------------------------------- ### Install Redis Server and Modules on Ubuntu/Debian Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/installation.md Installs the base Redis server and the Redis modules package, which may include RedisJSON. This is an alternative if Redis Stack is not used. ```bash sudo apt install redis-server redis-modules ``` -------------------------------- ### Basic Rapyer Configuration with Async Redis Client Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/installation.md Demonstrates how to configure Rapyer models with an asynchronous Redis client. It's crucial to set `decode_responses=True` for proper string handling. ```python import redis.asyncio as redis from rapyer import AtomicRedisModel # Configure Redis connection redis_client = redis.Redis( host='localhost', port=6379, db=0, decode_responses=True # (1)! ) # Define your model class User(AtomicRedisModel): name: str age: int # Set the Redis client for your model after class declaration User.Meta.redis = redis_client ``` -------------------------------- ### Rapyer Setup Verification Script Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/installation.md A Python script to test the Rapyer setup by performing basic Redis operations. It creates, retrieves, and deletes a model instance. ```python import asyncio import redis.asyncio as redis from rapyer import AtomicRedisModel # Setup Redis client redis_client = redis.Redis( host='localhost', port=6379, db=0, decode_responses=True ) class TestModel(AtomicRedisModel): message: str # Set Redis client after class declaration TestModel.Meta.redis = redis_client async def test_setup(): try: # Test basic operations model = TestModel(message="Hello, Rapyer!") await model.asave() # Retrieve the model retrieved = await TestModel.aget(model.key) print(f"Success! Retrieved: {retrieved.message}") # Cleanup await model.adelete() print("Setup verification complete!") except Exception as e: print(f"Setup error: {e}") finally: await redis_client.aclose() if __name__ == "__main__": asyncio.run(test_setup()) ``` -------------------------------- ### Install Redis Stack on Ubuntu/Debian Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/installation.md Installs the Redis Stack server package on Ubuntu or Debian-based systems. This provides a Redis server with the JSON module enabled. ```bash sudo apt install redis-stack-server ``` -------------------------------- ### Global Rapyer Configuration with init_rapyer Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/installation.md Shows how to initialize multiple Rapyer models with a single Redis client using `init_rapyer`. This function can also set a default TTL for all models. ```python import redis.asyncio as redis from rapyer import AtomicRedisModel, init_rapyer # Define your models first class User(AtomicRedisModel): name: str age: int class Session(AtomicRedisModel): user_id: str data: dict = {} # Initialize all models with a Redis client redis_client = redis.Redis( host='localhost', port=6379, db=0, decode_responses=True ) # This will set a Redis client for ALL AtomicRedisModel classes init_rapyer(redis=redis_client) # Or use a Redis URL string init_rapyer(redis="redis://localhost:6379/0") # Or set both Redis client and TTL for all models init_rapyer(redis=redis_client, ttl=3600) # 1 hour TTL for all models ``` -------------------------------- ### Integrate Rapyer Initialization and Teardown in Application Lifecycle (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/initialization.md Demonstrates how to integrate `init_rapyer` and `teardown_rapyer` within an application's main lifecycle, ensuring proper setup at startup and cleanup at shutdown. ```python import asyncio from rapyer import init_rapyer, teardown_rapyer, AtomicRedisModel class User(AtomicRedisModel): name: str age: int async def main(): try: # Initialize at application startup await init_rapyer(redis="redis://localhost:6379/0") # Your application logic user = User(name="Alice", age=25) await user.asave() # Application operations... finally: # Always cleanup at application shutdown await teardown_rapyer() if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Plugin System Example with SafeLoad in Python Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/safe-load-fields.md Presents a practical example of using SafeLoad within a plugin system. It demonstrates loading a plugin and returning a default handler if the primary handler field fails to load. ```python class Plugin(AtomicRedisModel): handler: SafeLoad[Optional[type]] = None name: str async def load_plugin(plugin_id: str): plugin = await Plugin.aget(plugin_id) if "handler" in plugin.failed_fields: return DefaultHandler() return plugin.handler() if plugin.handler else DefaultHandler() ``` -------------------------------- ### Development Redis Docker Compose Configuration Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/installation.md A Docker Compose file to set up a Redis Stack service for development environments. This simplifies the process of running Redis with the necessary modules. ```yaml version: '3.8' services: redis: image: redis/redis-stack-server:latest ports: - "6379:6379" environment: - REDIS_ARGS=--save 60 1000 ``` -------------------------------- ### Comparing afind() Performance vs. Individual get() in Python Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/crud-operations.md Illustrates the performance difference between using `afind()` for batch retrieval and making individual `get()` operations. `afind()` is significantly more efficient for multiple models due to reduced network overhead. This example shows an inefficient approach and the recommended efficient batch retrieval. ```python async def performance_example(): # ❌ Inefficient for multiple models user_keys = await User.afind_keys() users = [] for key in user_keys: # Multiple network round-trips user = await User.aget(key) users.append(user) # ✅ Efficient batch retrieval users = await User.afind() # Single network round-trip print(f"Retrieved {len(users)} users efficiently") ``` -------------------------------- ### Pipeline Example for Batch Operations (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/atomic-actions.md Provides an example of using the pipeline feature for batch operations on a user object. This is suitable for multiple related changes that do not require complex conditional logic. ```python # Assuming 'user' is an instance of a model with apipeline method async with user.apipeline(): user.score += 100 user.achievements.append("New Achievement") user.stats["games_played"] = user.stats.aget("games_played", 0) + 1 ``` -------------------------------- ### Complete Rapyer Example in Python Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/index.md A full example showcasing the usage of Rapyer. It defines a Pydantic model `User` that inherits from `AtomicRedisModel`, demonstrates creating, saving, performing atomic operations (append, extend, update), and loading a model from Redis. ```python import asyncio from rapyer import AtomicRedisModel from typing import List, Dict class User(AtomicRedisModel): name: str age: int tags: List[str] = [] metadata: Dict[str, str] = {} async def main(): # Create and save user = User(name="Alice", age=25) await user.asave() # Atomic operations await user.tags.aappend("developer") # (1)! await user.tags.aextend(["python", "redis"]) await user.metadata.aupdate(team="backend", level="senior") # (3)! # Load and verify loaded = await User.aget(user.key) print(f"User: {loaded.name}, Tags: {loaded.tags}") # (4)! if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Example Usage of AtomicRedisModel Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/api/atomic-redis-model.md Demonstrates the basic CRUD operations and advanced features like pipelines and locks with `AtomicRedisModel`. ```APIDOC ## Example Usage: AtomicRedisModel ### Description This example illustrates the fundamental usage of `AtomicRedisModel`, including creating, saving, retrieving, updating, and performing atomic operations using pipelines and locks. ### Request Example ```python from rapyer import AtomicRedisModel from typing import List, Dict class User(AtomicRedisModel): name: str email: str age: int = 0 tags: List[str] = [] settings: Dict[str, str] = {} # Create and save a new user instance user = User(name="John", email="john@example.com", age=30) await user.asave() # Retrieve the user instance from Redis using its key retrieved_user = await User.aget(user.key) # Update specific fields of the user instance atomically await user.aupdate(age=31, tags=["python", "redis"]) # Use the apipeline context manager for batch operations async with user.apipeline(): user.age += 1 user.tags.append("asyncio") user.settings["theme"] = "dark" # Use the alock context manager for exclusive access async with user.alock("profile_update", save_at_end=True): if user.age >= 25: user.tags.append("adult") user.settings["account_type"] = "premium" ``` ``` -------------------------------- ### Initialize Rapyer Models with Redis URL (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/initialization.md Demonstrates initializing Rapyer models using a Redis URL string, including support for authentication and Redis Cluster configurations. This provides a flexible way to connect to different Redis setups. ```python # Initialize with Redis URL string await init_rapyer(redis="redis://localhost:6379/0") # For production with authentication await init_rapyer(redis="redis://username:password@redis-server:6379/0") # Redis Cluster await init_rapyer(redis="redis://cluster-endpoint:6379/0") ``` -------------------------------- ### Pytest Parameterization Example Source: https://github.com/imaginary-cherry/rapyer/blob/main/CLAUDE.md Demonstrates how to use pytest's parameterize decorator for efficient test case management. It takes a list of parameter names and a list of parameter sets, allowing for multiple test scenarios with different inputs. ```python import pytest @pytest.parameterize(["param1", "param2"], [[1, 2], [3, 4]]) def test_example(param1, param2): assert param1 + param2 == 3 or param1 + param2 == 7 ``` -------------------------------- ### Example Usage of AtomicRedisModel Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/api/atomic-redis-model.md Demonstrates the basic workflow of creating, saving, retrieving, updating, and performing atomic operations on an `AtomicRedisModel`. It showcases the use of `asave`, `aget`, `aupdate`, `apipeline`, and `alock`. ```python from rapyer import AtomicRedisModel from typing import List, Dict class User(AtomicRedisModel): name: str email: str age: int = 0 tags: List[str] = [] settings: Dict[str, str] = {} # Create and save user = User(name="John", email="john@example.com", age=30) await user.asave() # Retrieve retrieved_user = await User.aget(user.key) # Update atomically await user.aupdate(age=31, tags=["python", "redis"]) # Batch operations async with user.apipeline(): user.age += 1 user.tags.append("asyncio") user.settings["theme"] = "dark" # Lock for exclusive access async with user.alock("profile_update", save_at_end=True): if user.age >= 25: user.tags.append("adult") user.settings["account_type"] = "premium" ``` -------------------------------- ### Install Rapyer Package Source: https://github.com/imaginary-cherry/rapyer/blob/main/README.md Installs the Rapyer package using pip. Requires Python 3.10+, a Redis server with the JSON module, and Pydantic v2. ```bash pip install rapyer ``` -------------------------------- ### Retrieving Models: Keys, All Instances, or Specific by Key in Python Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/crud-operations.md Explains the different methods for retrieving models: `afind_keys()` to get only the keys, `afind()` to load all model instances, and `aget()` to retrieve a specific model by its key. This is useful for managing data retrieval based on specific needs. ```python async def main(): # Get just the keys user_keys = await User.afind_keys() print(f"User keys: {user_keys}") # Get all user instances users = await User.afind() print(f"Loaded {len(users)} users") # Get specific user by key specific_user = await User.aget(user_keys[0]) print(f"Specific user: {specific_user.name}") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Create, Save, and Retrieve Multiple Models with Rapyer Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/api/rapyer-functions.md This Python example demonstrates how to define data models using Rapyer's AtomicRedisModel, create instances of these models, save them to Redis using `rapyer.ainsert`, and then retrieve multiple models of different types in a single call using `rapyer.afind`. It also shows how to access the retrieved model data. ```python import asyncio import rapyer from rapyer import AtomicRedisModel class User(AtomicRedisModel): name: str email: str class Order(AtomicRedisModel): user_id: str total: float class Product(AtomicRedisModel): name: str price: float async def main(): # Create and save models of different types user = User(name="Alice", email="alice@example.com") order = Order(user_id=user.key, total=150.00) product = Product(name="Laptop", price=999.99) await rapyer.ainsert(user, order, product) # Retrieve multiple models of different types in one call models = await rapyer.afind(user.key, order.key, product.key) print(f"Retrieved {len(models)} models:") for model in models: print(f" - {type(model).__name__}: {model.key}") # Models are returned in the same order as keys retrieved_user, retrieved_order, retrieved_product = models print(f"User: {retrieved_user.name}") print(f"Order total: ${retrieved_order.total}") print(f"Product: {retrieved_product.name}") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Universal Type System Example (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/README.md Showcases Rapyer's ability to handle various Python types, including lists, dataclasses, and dictionaries, with native Redis operations and automatic serialization. This contrasts with ORMs that may have limitations on complex or custom types. ```python # Rapyer - Any Python type works identically class User(AtomicRedisModel): scores: List[int] = [] # Native Redis operations config: MyDataClass = MyDataClass() # Auto-serialized metadata: Dict[str, Any] = {} # Native Redis operations # All types support the same atomic operations await user.config.set(new_config) # Automatic serialization await user.scores.aappend(95) # Native Redis LIST operations await user.metadata.aupdate(key="val") # Native Redis JSON operations ``` -------------------------------- ### Migration Example (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/setting-key-field.md Shows a Python code snippet illustrating migration considerations when adding a custom `Key` field to an existing Rapyer model. It highlights that existing records retain their auto-generated keys, while new records will use the specified custom key field. ```python # Before: Using auto-generated keys class User(AtomicRedisModel): name: str email: str # After: Adding custom key (existing records keep auto-generated keys) class User(AtomicRedisModel): email: Key[str] # New records will use email as key name: str ``` -------------------------------- ### Atomic Operations Example in Python Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/index.md Demonstrates how to perform atomic operations on Pydantic model fields stored in Redis using Rapyer. This prevents race conditions in concurrent environments by ensuring operations like list appends, dictionary updates, and increments are performed atomically. ```python # All operations are atomic - no race conditions possible await user.tags.aappend("python") # Atomic list append await user.metadata.aupdate(role="dev") # Atomic dict update user.score += 10 await user.score.asave() # Atomic increment ``` -------------------------------- ### Lock with Operation Names for Concurrent User Updates (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/atomic-actions.md This Python example illustrates how to use operation names with the lock context manager to allow concurrent, yet controlled, updates to a User model. Different operations like 'profile_update', 'view_tracking', and 'login_update' can run in parallel for the same user, but operations with the same name (e.g., multiple 'profile_update' calls) will be serialized to prevent conflicts. The lock is scoped to the model instance. ```python class User(AtomicRedisModel): name: str email: str profile_views: int = 0 last_login: str = "" settings: Dict[str, str] = {} # These can run concurrently (different lock actions) async def update_profile(user_key: str, new_name: str, new_email: str): async with User.alock_from_key(user_key, "profile_update") as user: # Model state refreshed from Redis user.name = new_name user.email = new_email async def track_page_view(user_key: str): async with User.alock_from_key(user_key, "view_tracking") as user: # Independent operation with separate lock user.profile_views += 1 async def update_login_time(user_key: str): async with User.alock_from_key(user_key, "login_update") as user: from datetime import datetime user.last_login = datetime.now().isoformat() # This would be serialized with other "profile_update" locks on the SAME user async def another_profile_update(user_key: str): async with User.alock_from_key(user_key, "profile_update") as user: # Must wait for other "profile_update" operations on this specific user to complete user.settings["theme"] = "dark" ``` -------------------------------- ### Handle Multiple Redis Instances with Rapyier (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/initialization.md Shows how to manage different Redis instances for various models within Rapyier. It covers default initialization and overriding specific models with custom Redis connections. ```python # For models that need different Redis instances await init_rapyer(redis="redis://localhost:6379/0") # Default for most models # Override specific models if needed SpecialModel.Meta.redis = redis.from_url("redis://special-redis:6379/0") ``` -------------------------------- ### Initialize Rapyier Before Using Models (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/initialization.md Demonstrates the correct way to initialize Rapyier using `init_rapyer` before instantiating and using models like `User`. It also shows the incorrect approach to highlight potential issues. ```python await init_rapyer(redis="redis://localhost:6379/0") user = User(name="Alice") # Now ready to use # ✗ Don't use models before initialization user = User(name="Alice") # May not work correctly await init_rapyer(redis="redis://localhost:6379/0") ``` -------------------------------- ### Key Uniqueness Example (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/setting-key-field.md Illustrates the importance of ensuring uniqueness for custom key fields in Python Rapyer models. It shows a 'good' example using a naturally unique 'email' field and a 'risky' example using a potentially non-unique 'name' field as the primary key. ```python # ✅ Good: Emails are naturally unique class User(AtomicRedisModel): email: Key[str] # Safe choice name: str # ⚠️ Risky: Names might not be unique class User(AtomicRedisModel): name: Key[str] # Could cause conflicts! email: str ``` -------------------------------- ### Advanced Rapyer Initialization with Connection Pool (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/initialization.md Illustrates advanced configuration of Rapyer models using a custom Redis client with a connection pool. This allows for fine-grained control over Redis connection management. ```python # Custom connection pool with init_rapyer redis_client = redis.Redis.from_url( "redis://localhost:6379/0", max_connections=20, decode_responses=True ) await init_rapyer(redis=redis_client, ttl=3600) ``` -------------------------------- ### FastAPI Lifespan Integration for Rapyer Initialization (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/initialization.md Shows how to use FastAPI's lifespan context manager to automatically initialize Rapyer at application startup and tear it down upon shutdown. This ensures seamless integration with web applications. ```python from fastapi import FastAPI from contextlib import asynccontextmanager from rapyer import init_rapyer, teardown_rapyer @asynccontextmanager async def lifespan(app: FastAPI): # Startup await init_rapyer(redis="redis://localhost:6379/0") yield # Shutdown await teardown_rapyer() app = FastAPI(lifespan=lifespan) ``` -------------------------------- ### Log Entry Model with Timestamp Key (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/setting-key-field.md Provides a Python example of using the `Key` field annotation with a `datetime` object as the primary key for a `LogEntry` model. The example demonstrates defining the model and saving an instance with the current timestamp as its unique identifier. ```python from datetime import datetime from rapyer.fields import Key class LogEntry(AtomicRedisModel): timestamp: Key[datetime] level: str message: str source: str # Usage log = LogEntry( timestamp=datetime.now(), level="INFO", message="Application started", source="main.py" ) await log.asave() # Stored with timestamp as the Redis key ``` -------------------------------- ### User Model with Email as Key (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/setting-key-field.md Illustrates a practical example of using the `Key` field annotation in Python, where the 'email' field is designated as the primary key for the `User` model. It includes model definition and usage example, showing how the email is used as the Redis key upon saving. ```python from rapyer.fields import Key class User(AtomicRedisModel): email: Key[str] # Email serves as the unique identifier name: str age: int preferences: dict = {} # Usage user = User(email="alice@example.com", name="Alice", age=30) await user.asave() # Stored with email as the Redis key ``` -------------------------------- ### Checking if AtomicRedisModel is Nested (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/api/atomic-redis-model.md Provides an example of the synchronous `is_inner_model()` method, which returns `True` if the model instance is a nested field within another model, and `False` otherwise. ```python if user.is_inner_model(): print("This is a nested model") ``` -------------------------------- ### AtomicRedisModel Instance Methods Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/api/atomic-redis-model.md Provides documentation for instance methods of the AtomicRedisModel, covering data persistence, retrieval, and manipulation. ```APIDOC ## AtomicRedisModel Instance Methods ### Description Methods that operate on an instance of an `AtomicRedisModel` for interacting with Redis. ### `asave()` #### Method `async` #### Endpoint N/A (Instance Method) #### Parameters None #### Request Example ```python await user.asave() ``` #### Response ##### Success Response (200) - **Self** (AtomicRedisModel) - The saved model instance. ##### Response Example ```json { "name": "John", "age": 30 } ``` ### `aload()` #### Method `async` #### Endpoint N/A (Instance Method) #### Parameters None #### Request Example ```python await user.aload() ``` #### Response ##### Success Response (200) - **Self** (AtomicRedisModel) - The loaded model instance. ##### Errors - `CantSerializeRedisValueError`: If Redis value cannot be deserialized. ##### Response Example ```json { "name": "Jane", "age": 25 } ``` ### `adelete()` #### Method `async` #### Endpoint N/A (Instance Method) #### Parameters None #### Request Example ```python success = await user.adelete() ``` #### Response ##### Success Response (200) - **bool** - `True` if deletion was successful, `False` otherwise. (Note: Documentation states `bool` return, but typically delete operations return status or raise errors on failure). ##### Response Example ```json true ``` ### `aset_ttl(ttl)` #### Method `async` #### Endpoint N/A (Instance Method) #### Parameters - **ttl** (int) - Required - Time-to-live in seconds. #### Request Example ```python await user.aset_ttl(3600) # Within pipeline context async with user.apipeline(): user.score += 100 await user.aset_ttl(7200) ``` #### Response ##### Success Response (200) No explicit return value documented, implies success via absence of error. ### `aduplicate()` #### Method `async` #### Endpoint N/A (Instance Method) #### Parameters None #### Request Example ```python user_copy = await user.aduplicate() ``` #### Response ##### Success Response (200) - **Self** (AtomicRedisModel) - A new, duplicated model instance. ##### Response Example ```json { "name": "John", "age": 30 } ``` ### `aduplicate_many(num)` #### Method `async` #### Endpoint N/A (Instance Method) #### Parameters - **num** (int) - Required - Number of duplicates to create. #### Request Example ```python user_copies = await user.aduplicate_many(5) ``` #### Response ##### Success Response (200) - **list[Self]** (list) - A list of duplicated model instances. ##### Response Example ```json [ { "name": "John", "age": 30 }, { "name": "John", "age": 30 } ] ``` ### `update(**kwargs)` #### Method Synchronous #### Endpoint N/A (Instance Method) #### Parameters - **kwargs** (dict) - Required - Field values to update. #### Request Example ```python user.update(name="Jane", age=25) ``` #### Response ##### Success Response (200) No explicit return value documented, implies success via absence of error. ### `aupdate(**kwargs)` #### Method `async` #### Endpoint N/A (Instance Method) #### Parameters - **kwargs** (dict) - Required - Field values to update. #### Request Example ```python await user.aupdate(name="Jane", age=25) ``` #### Response ##### Success Response (200) No explicit return value documented, implies success via absence of error. ### `is_inner_model()` #### Method Synchronous #### Endpoint N/A (Instance Method) #### Parameters None #### Request Example ```python if user.is_inner_model(): print("This is a nested model") ``` #### Response ##### Success Response (200) - **bool** - `True` if the model is nested, `False` otherwise. ##### Response Example ```json true ``` ``` -------------------------------- ### Example of Inefficient Filtering Without Indexing Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/indexing-fields.md Demonstrates how models are retrieved by key or by fetching all models and filtering manually, which is inefficient for large datasets. This highlights the benefit of using indexed fields. ```python # You can only retrieve by key user = await User.aget("User:abc123") # Or get ALL users and filter manually (inefficient) all_users = await User.afind() older_users = [u for u in all_users if u.age > 30] ``` -------------------------------- ### Rapyer Atomic Operations Examples Source: https://github.com/imaginary-cherry/rapyer/blob/main/README.md Illustrates core atomic operations in Rapyer, including appending to lists, updating dictionaries, and setting values, all of which are race-condition safe. ```python # These operations are atomic and race-condition safe await user.tags.aappend("python") # Add to list await user.metadata.aupdate(role="dev") # Update dict await user.score.set(100) # Set value ``` -------------------------------- ### Automatic Redis Type Conversion for Model Fields Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/api/atomic-redis-model.md Rapyer automatically converts standard Python types used in model fields to their corresponding Redis-compatible types. For example, lists become `RedisList` and dictionaries become `RedisDict`. ```python class User(AtomicRedisModel): name: str # Becomes RedisStr tags: List[str] # Becomes RedisList[str] settings: Dict[str, str] # Becomes RedisDict[str, str] ``` -------------------------------- ### Rapyer Model Meta Configuration Options Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/defining-models.md Illustrates the available Meta class configurations for Rapyer's AtomicRedisModel. This includes setting the Redis client instance and the time-to-live (TTL) in seconds. Requires 'redis.asyncio' and 'rapyer'. ```python class User(AtomicRedisModel): name: str age: int # Available Meta configurations User.Meta.redis = redis_client # Redis client instance User.Meta.ttl = 3600 # Time-to-live in seconds (optional) ``` -------------------------------- ### Environment-Based Rapyer Initialization (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/initialization.md Configures Rapyer initialization based on environment variables for Redis URL and TTL. This promotes flexible and dynamic configuration across different deployment environments. ```python import os from rapyer import init_rapyer async def setup_redis(): redis_url = os.getenv("REDIS_URL", "redis://localhost:6379/0") ttl = int(os.getenv("REDIS_TTL", "3600")) # Default 1 hour await init_rapyer(redis=redis_url, ttl=ttl) ``` -------------------------------- ### Checking Failed Fields After Loading in Python Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/safe-load-fields.md Illustrates how to retrieve a model instance and check the `failed_fields` attribute to identify which fields could not be loaded. Provides an example of using a fallback mechanism if a critical field fails. ```python plugin = await Plugin.aget("plugin-123") if plugin.failed_fields: print(f"Could not load: {plugin.failed_fields}") # Use a fallback plugin.handler_class = DefaultHandler ``` -------------------------------- ### Single Model Deletion in Python Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/crud-operations.md Shows how to delete a single model instance from Redis using the `adelete()` method. This operation is performed asynchronously. The example includes creating a user, deleting it, and then verifying the deletion by attempting to retrieve the user again. ```python async def main(): # Create and save user user = User(name="Eve", age=22, email="eve@example.com") await user.asave() user_key = user.key # Delete from Redis await user.adelete() print(f"Deleted user with key: {user_key}") # Verify deletion deleted_user = await User.aget(user_key) if deleted_user is None: print("User successfully deleted") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Updating Model Data with asave() in Python Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/crud-operations.md Demonstrates how to update existing model data in Rapyer. It involves retrieving a model, modifying its attributes, and then calling `asave()` to persist the changes. The example includes verification of the update by fetching the model again. ```python async def main(): # Create and save user user = User(name="Charlie", age=28, email="charlie@example.com") await user.asave() # Update and save user.age = 29 user.email = "charlie.new@example.com" await user.asave() # Verify the update updated_user = await User.aget(user.key) print(f"Updated age: {updated_user.age}, email: {updated_user.email}") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Rapyer Data Modeling and Querying with Python Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/indexing-fields.md This Python script demonstrates how to define an atomic Redis model using Rapyer, initialize the connection, insert multiple user records, and perform various find operations based on different criteria. It requires the 'rapyer' library and a running Redis instance on localhost:6379. ```python import asyncio from datetime import datetime from typing import Annotated from rapyer import AtomicRedisModel, Index, init_rapyer, teardown_rapyer class User(AtomicRedisModel): name: Annotated[str, Index] age: Annotated[int, Index] email: Annotated[str, Index] status: Annotated[str, Index] = "active" score: Annotated[float, Index] = 0.0 # Non-indexed field internal_id: str = "" async def main(): # Initialize rapyer - REQUIRED for indexed fields await init_rapyer(redis="redis://localhost:6379/0") try: # Create and save users users = [ User(name="Alice", age=25, email="alice@example.com", score=85.5), User(name="Bob", age=30, email="bob@example.com", status="inactive", score=92.0), User(name="Charlie", age=35, email="charlie@example.com", score=78.3), User(name="Diana", age=28, email="diana@example.com", score=95.8) ] await User.ainsert(*users) # Find active users active = await User.afind(User.status == "active") print(f"Active users: {[u.name for u in active]}") # Alice, Charlie, Diana # Find users older than 27 older = await User.afind(User.age > 27) print(f"Users older than 27: {[u.name for u in older]}") # Bob, Charlie, Diana # Find young active users with high scores special = await User.afind( (User.age <= 30) & (User.status == "active") & (User.score >= 80) ) print(f"Young active high-scorers: {[u.name for u in special]}") # Alice, Diana finally: await teardown_rapyer() if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Define Event Model with Annotated Key Field (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/setting-key-field.md Shows an alternative syntax for using the `Key` field annotation with `typing.Annotated` in Python. This example sets 'created_at' of type `datetime` as the primary key for an `Event` model. ```python from typing import Annotated from datetime import datetime from rapyer import AtomicRedisModel from rapyer.fields import Key class Event(AtomicRedisModel): created_at: Annotated[datetime, Key()] # datetime as primary key event_name: str description: str duration_minutes: int = 60 ``` -------------------------------- ### Conditional Rapyer Configuration Based on Environment (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/initialization.md Implements conditional initialization of Rapyer based on the environment variable 'ENV'. This allows for different Redis configurations (e.g., production, testing, local) to be applied dynamically. ```python async def initialize_models(): if os.getenv("ENV") == "production": # Production Redis with TTL await init_rapyer( redis="redis://prod-redis:6379/0", ttl=7200 # 2 hours ) elif os.getenv("ENV") == "testing": # Test Redis without TTL await init_rapyer(redis="redis://test-redis:6379/1") else: # Local development await init_rapyer(redis="redis://localhost:6379/0") ``` -------------------------------- ### Set Global TTL for Rapyer Models (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/initialization.md Shows how to set a global Time-To-Live (TTL) for all Rapyer models during initialization. This ensures data expires automatically after a specified duration, managed centrally. ```python # Set TTL for all models (1 hour) await init_rapyer(redis=redis_client, ttl=3600) # TTL only (if Redis already configured) await init_rapyer(ttl=1800) # 30 minutes ``` -------------------------------- ### RedisDatetimeTimestamp for Efficient Timestamp Storage Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/api/redis-types.md Details RedisDatetimeTimestamp, a Redis-backed datetime type that stores values as Unix timestamps (floats) for efficient storage and external compatibility. It warns about potential timezone information loss and provides an example of its usage. ```python from rapyer.types import RedisDatetimeTimestamp from datetime import datetime, timezone class Event(AtomicRedisModel): name: str created_at: RedisDatetimeTimestamp # Stored as timestamp float updated_at: RedisDatetimeTimestamp = None class Analytics(AtomicRedisModel): event_name: str timestamp: RedisDatetimeTimestamp # Efficient timestamp storage session_start: RedisDatetimeTimestamp session_end: RedisDatetimeTimestamp = None # Create with current time analytics = Analytics( event_name="user_login", timestamp=datetime.now(), session_start=datetime.now(timezone.utc) # Timezone will be lost ) await analytics.asave() # Datetime operations work normally duration = analytics.session_end - analytics.session_start if analytics.session_end else None # Efficient timestamp-based queries and storage epoch_time = analytics.timestamp.timestamp() ``` -------------------------------- ### Finding All Model Keys and Instances in Python Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/crud-operations.md Demonstrates how to retrieve all keys associated with a specific model class (e.g., `User`) using `afind_keys()`, and then load all instances efficiently using `afind()`. This is useful for iterating over all records of a certain type stored in Redis. Requires `asyncio` and a `User` model. ```python async def main(): # Create multiple users user1 = User(name="Alice", age=25, email="alice@example.com") user2 = User(name="Bob", age=30, email="bob@example.com") user3 = User(name="Charlie", age=35, email="charlie@example.com") await user1.asave() await user2.asave() await user3.asave() # Find all User keys in Redis user_keys = await User.afind_keys() print(f"Found {len(user_keys)} users: {user_keys}") # Load all users manually (less efficient approach) users = [] for key in user_keys: user = await User.aget(key) users.append(user) for user in users: print(f"User: {user.name}, Age: {user.age}") # More efficient approach using afind() all_users = await User.afind() print(f"Loaded all users efficiently: {len(all_users)}") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### RedisInt Type for Integer Data Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/redis-types.md Details the RedisInt type, inheriting from int and RedisType, for integer numeric values. It enables atomic increment operations and supports atomic save and load. The example demonstrates flexible typing and atomic increment. ```python from typing import Union from rapyer.model import AtomicRedisModel from rapyer.types import RedisInt class Counter(AtomicRedisModel): count: Union[int, RedisInt] = 0 score: Union[int, RedisInt] = 100 # Example usage (assuming 'counter' is an instance of Counter): # await counter.count.asave() # await counter.count.aload() # await counter.count.aincrease(5) ``` -------------------------------- ### Immutability Warning Example (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/setting-key-field.md Demonstrates the potential issues with changing a custom key field value after a model has been saved in Python Rapyer. Modifying the key field can lead to unexpected behavior, such as creating a new record instead of updating the existing one. ```python user = User(email="john@example.com", name="John") await user.asave() # ❌ Avoid changing the key field value user.email = "john.doe@example.com" # This changes the primary key! await user.asave() # This might create a new record instead of updating ``` -------------------------------- ### Initialize Rapyer Models with Redis Client (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/initialization.md Configures all AtomicRedisModel classes with a provided Redis client and optional TTL. This function is essential for setting up Redis connections and default time-to-live values across your application. ```python import redis.asyncio as redis from rapyer import AtomicRedisModel, init_rapyer # Define your models first class User(AtomicRedisModel): name: str age: int class Session(AtomicRedisModel): user_id: str data: dict = {} class Product(AtomicRedisModel): name: str price: float # Initialize all models with Redis client redis_client = redis.Redis( host='localhost', port=6379, db=0, decode_responses=True ) # This sets Redis client for ALL AtomicRedisModel classes await init_rapyer(redis=redis_client) ``` -------------------------------- ### Ensure Rapyier Teardown with try-finally (Python) Source: https://github.com/imaginary-cherry/rapyer/blob/main/docs/documentation/initialization.md Illustrates the importance of using a `try...finally` block to ensure `teardown_rapyer` is always called, even if errors occur during application logic. This prevents resource leaks. ```python try: await init_rapyer(redis="redis://localhost:6379/0") # Application logic finally: await teardown_rapyer() # Always cleanup ```