Skip to content

Changelog

[1.1.7]

✨ Added

  • Global rapyer.afind() Function: Added rapyer.afind() function to retrieve multiple models of different types by their keys in a single bulk operation.
  • Supports fetching models of heterogeneous types in one call
  • Automatically refreshes TTL for models with refresh_ttl enabled
  • Raises KeyNotFound if any key is missing in Redis
  • Raises RapyerModelDoesntExistError if a key refers to an unregistered model class
  • Example: models = await rapyer.afind("UserModel:123", "OrderModel:456")

  • Pipeline Operations for Non-Redis-Native Types: Added full pipeline support for List[Any] and Dict[str, Any] fields that store serialized data.

  • Supports append(), extend(), insert(), index assignment for lists
  • Supports update(), key assignment, pop() for dicts
  • Direct field assignment within pipeline context (e.g., redis_model.field = value)
  • All operations are atomic and only committed when the pipeline exits
  • Example: async with model.apipeline() as m: m.mixed_list.append({"key": "value"})

  • Enhanced Pipeline Operations for Redis-Native Types: Added in-place arithmetic operations (+=, -=) within pipeline context for Redis-native types.

  • RedisDatetime and RedisDatetimeTimestamp: Support += and -= with timedelta for atomic date arithmetic
  • RedisFloat: Support +=, -=, *=, /= for atomic numeric operations
  • RedisInt: Support +=, -= for atomic increment/decrement operations
  • RedisStr: Support += for atomic string append operations

  • FakeRedis Support: Added support for fakeredis library for unit testing without a real Redis instance.

  • Supports rapyer.afind(), rapyer.ainsert(), rapyer.aget() with FakeRedis
  • Supports pipeline operations with FakeRedis
  • Enables faster test execution without Redis dependency

🐛 Fixed

  • TTL Support in ainsert: The ainsert() method now automatically sets TTL on inserted models based on their model configuration.

🔄 Changed

  • Model.afind() Strict Key Validation: When specific keys are passed to Model.afind(), it now raises KeyNotFound if any key is missing in Redis. Previously, missing keys were silently ignored.

[1.1.6]

✨ Added

  • afind Key-Based Search: Added support for passing keys directly to afind() to retrieve specific models by their keys, without requiring a Redis Search query.
  • Supports both full keys (Model:uuid) and primary key values (uuid)
  • Example: await Model.afind(key1, key2) or await Model.afind("uuid1", "uuid2")
  • Note: If both keys and expressions are provided, expressions are ignored with a warning
  • Logger Configuration in init_rapyer: Added logger parameter to init_rapyer() function to configure the rapyer logger with a custom logger's level and handlers.
  • Example: await init_rapyer(redis=redis_client, logger=my_logger)
  • aset_ttl Method: Added aset_ttl(ttl) method to AtomicRedisModel for manually setting or updating the TTL of a model instance.
  • Example: await model.aset_ttl(3600) sets TTL to 1 hour
  • Only works on top-level models (raises RuntimeError if called on inner models)

🐛 Fixed

  • afind with Non-JSON Keys: Fixed afind to gracefully skip non-JSON keys (e.g., lock keys like Model:key:lock) that match the model's key pattern but contain plain string values instead of JSON.
  • afind with Invalid JSON Schema: Fixed afind to skip entries with JSON values that don't match the model schema, preventing validation errors from crashing the entire operation.
  • Global afind Bug: Fixed a bug where afind would fail when encountering keys that were deleted during the operation.
  • apipeline bug: Fixed a bug for apipeline when we want to ignore deleted model.

[1.1.5]

✨ Added

  • RedisList.remove_range(): Added remove_range(start, end) method to RedisList for removing a range of items (like del list[start:end]).
  • Works within pipeline context for atomic operations
  • Supports negative indices (count from end)
  • Uses Lua script internally for race-condition-free execution
  • Example: playlist.songs.remove_range(1, 3) removes items at indices 1 and 2
  • SafeLoad Field Annotation: Added SafeLoad[T] annotation for fields that should gracefully handle deserialization failures instead of raising exceptions.
  • When a SafeLoad field fails to deserialize, it returns None and logs a warning instead of crashing
  • Failed field names are tracked in the model's failed_fields property
  • Example: safe_type_field: SafeLoad[Optional[Type[str]]] = Field(default=None)
  • Model-Wide SafeLoad Configuration: Added safe_load_all option to RedisConfig Meta class to treat all non-Redis-supported fields as SafeLoad fields.
  • Example: Meta = RedisConfig(safe_load_all=True)

🛠️ Technical Improvements

  • Pipeline Transactions: Pipelines now use Redis MULTI/EXEC transactions for atomic execution of batched operations.
  • NOSCRIPT Error Recovery: Pipelines automatically recover from NOSCRIPT errors (e.g., after Redis restart) by re-registering Lua scripts and retrying failed script commands.
  • Smart Field Serialization: Fields that can be JSON-serialized are now stored as native JSON instead of being pickled. This improves Redis data readability and interoperability with other systems.
  • Pickle is only used for fields that cannot be JSON-serialized (e.g., type objects, custom classes)
  • Backward compatible: existing pickled data is automatically detected and loaded correctly, We will remove the compatibility in version 1.2.0

[1.1.4]

✨ Added

  • Global alock_from_key Function: Added rapyer.alock_from_key() function to create locks without needing a model instance. This allows locking by key directly for operations that don't require the model class.
  • Model TTL Extension on Redis Actions: Models now automatically extend their TTL when performing Redis actions, keeping frequently accessed models alive longer.

🔧 Improved

  • Redis Locking Mechanism: Now using formal Redis lock for more persistent and reliable locking mechanism.

🐛 Fixed

  • apipeline KeyNotFound: Fixed apipeline for cases where model doesn't exists in redis.
  • rapyer.get: Fix a bug in the rapyer.get() function.
  • Context Manager Annotations: Fixed type annotations for context managers to properly reflect their return types.
  • RedisBytes Pipeline: Fixed bug in RedisBytes when used within pipeline context.
  • RedisList Pipeline: Fixed bug in RedisList when used within pipeline context.
  • afind Nested Fields: Fixed afind to support filtering on nested fields (e.g., afind(User.parent.age > 20)).
  • Key and Index Type Checking: Fixed type checking support for Key[T] and Index[T] annotations. IDEs now correctly recognize Index[str] as str instead of _IndexType[str].

🛠️ Technical Improvements

  • Test Coverage: Added tests for full coverage.

[1.1.3]

Reupload of 1.1.2

[1.1.2]

We yanked the 1.1.1 release due to a bug in the pipeline context manager. This is the fixed version.

[1.1.1]

In this version we officaly starting the support for bulk operation on multiple models. In line with our philsophy of atomic operations.

✨ Added

  • Bulk Insert: We added the ainsert classmethod to AtomicRedisModel to insert multiple models in a single operation.
  • Bulk delete: We added the adelete_many classmethod to AtomicRedisModel to delete many objects in a single operation.
  • Flexible Bulk Delete: The adelete_many method now supports both model instances and Redis keys as arguments, allowing for more flexible bulk deletion operations. You can mix and match models and keys in a single call.
  • RedisFloat Type: Added support for float Redis types with atomic increment operations and in-place arithmetic operations (+=, -=, *=, /=) within pipeline contexts.
  • Global ainsert Function: Added rapyer.ainsert() function to insert models of any type in a single operation, enabling bulk inserts of heterogeneous model types.
  • Filtering in Search: Added support for filtering in afind() method using expressions, allowing you to search for models that match specific criteria with operators like ==, !=, >, <, >=, <= and logical operators (&, |, ~).
  • RedisDatetimeTimestamp Type: Added new RedisDatetimeTimestamp type that stores datetime values as timestamps (floats) in Redis instead of ISO strings. This provides more efficient storage and better compatibility with external systems that expect timestamp format. Note: timezone information is lost during conversion as timestamps represent UTC moments in time.

⚠️ Deprecated

  • Function Name Migration to Async: The following functions have been renamed to follow async naming conventions. We moved to a strict convention to support non async models in a future version. Old names are deprecated and will be removed in a future version:
  • save()asave() - Save model instance to Redis
  • load()aload() - Load model data from Redis
  • delete()adelete() - Delete model instance from Redis
  • get()aget() - Retrieve model instance by key (class method)
  • duplicate()aduplicate() - Create a duplicate of the model
  • duplicate_many()aduplicate_many() - Create multiple duplicates
  • delete_by_key()adelete_by_key() - Delete model by key (class method)
  • lock()alock() - Create lock context manager for model
  • lock_from_key()alock_from_key() - Create lock context manager from key (class method)
  • pipeline()apipeline() - Create pipeline context manager for batched operations

[1.1.0]

✨ Added

  • Version Support: Support more python versions, pydantic and redis versions, including tests in pipeline for each version.

🐛 Fixed

  • Rapyer init: Fix a bug for init_rapyer when using url.

🔄 Changed

  • BREAKING: We stopped using RedisListType, RedisIntType, etc. instead, you can use RedisList directly with full IDE support.

[1.0.4]

✨ Added

  • In-Place Pipeline Changes: Added support for in-place pipeline operations for all Redis types
  • Any action performed on Redis models within a pipeline now directly affects the Redis model instance
  • Both awaitable and non-awaitable functions now support in-place modifications during pipeline execution
  • Support for generic fields for dict and list: List and Dict now support any serializable type as a genric type
  • Model afind: We added afind function to extract all models of a specific class. In the future, we will also add options to use filters in the afind

[1.0.3]

✨ Added

  • Custom Primary Keys: Added Key annotation to specify custom fields as primary keys instead of auto-generated ones
  • Enhanced IDE Typing Support: Added specialized Redis types (RedisListType, RedisDictType, etc.) for better IDE autocompletion and type hinting
  • Global Model Retrieval: Added rapyer.get() function to retrieve any Redis model instance by its key without needing to know the specific model class
  • Example: model = await rapyer.get("UserModel:12345")
  • Model Discovery: Added find_redis_models() function to discover all Redis model classes in the current environment
  • Key Discovery: Added find_keys() class method to retrieve all Redis keys for a specific model class

[1.0.2] - 2025-11-05

✨ Added

  • Inheritance Model Support: Added support for inheritance models - models that inherit from a Redis model still create a Redis model with full functionality
  • Global Configuration: Added init_rapyer() function to set Redis client and TTL for all models at once
  • Accepts Redis client instance or connection string (e.g., "redis://localhost:6379")
  • Allows setting global TTL for all Redis models
  • Example: init_rapyer(redis="redis://localhost:6379", ttl=3600)
  • Atomic Updates: Added aupdate() method to AtomicRedisModel for selective field updates without loading the entire model
  • Enables direct field updates in Redis: await model.aupdate(field1="value", field2=123)
  • Maintains type safety and validation during updates
  • Uses Redis JSON path operations for efficient field-only updates
  • All field updates in a single aupdate call are atomic

🐛 Fixed

  • Redis Type Override Bug: Fixed a bug that overrode the Redis type in lock and pipeline operations
  • Redis List Bug: Fixed a bug for extending an empty list

⚠️ Compatibility Notice

  • Pydantic Version Constraint: This version supports Pydantic up to 2.12.0 due to internal logic changes in newer versions
  • A future release will include support for multiple Pydantic versions
  • All previous versions also have the same Pydantic 2.12.0 limitation

[1.0.1] - 2025-11-04

✨ Added

  • Non-Serializable Type Support: Added support for non-serializable types (like type and other pickleable objects)
  • Pickle Storage: Non-serializable types are now stored in Redis as pickle data for proper serialization
  • Optional Field Support: Added support for optional fields in Redis types

[1.0.0] - 2025-11-02

🚀 Major Changes - Native BaseModel Integration

This release introduces native BaseModel compatibility, making Redis types work seamlessly with Pydantic models without requiring explicit initialization.

✨ Added

  • Native Redis Type Integration: Redis types now work directly with BaseModel - no need to initialize with "", 0, etc.
  • Direct Field Assignment: Use simple assignment like name: RedisStr = "" instead of name: RedisStr = ""
  • Enhanced Nested Operations: Support for saving inner fields directly with model.lst[1].asave()
  • Simplified Type Declarations: All Redis types (RedisStr, RedisInt, RedisList, RedisDict, RedisBytes) now support native Python value assignment

🔄 Changed

  • BREAKING: Removed set() function - Redis types now update automatically when modified
  • Simplified API: Redis type actions now automatically update the Redis store

🛠️ Technical Improvements

  • Streamlined type validation and serialization
  • Improved IDE support for Redis types with native Python syntax
  • Better integration with Pydantic's validation system
  • Reduced boilerplate code for Redis type usage