diff --git a/changelog.d/373.feature b/changelog.d/373.feature new file mode 100644 index 00000000..0bde78ca --- /dev/null +++ b/changelog.d/373.feature @@ -0,0 +1 @@ +The interface corresponding to hset, hdel of redis-py has been created. \ No newline at end of file diff --git a/django_redis/cache.py b/django_redis/cache.py index d3e8708d..533b2cdb 100644 --- a/django_redis/cache.py +++ b/django_redis/cache.py @@ -183,3 +183,15 @@ def close(self, **kwargs): @omit_exception def touch(self, *args, **kwargs): return self.client.touch(*args, **kwargs) + + @omit_exception + def hdel(self, *args, **kwargs): + return self.client.hdel(*args, **kwargs) + + @omit_exception + def hset(self, *args, **kwargs): + return self.client.hset(*args, **kwargs) + + @omit_exception + def hget(self, *args, **kwargs): + return self.client.hget(*args, **kwargs) diff --git a/django_redis/client/default.py b/django_redis/client/default.py index 1df90a27..3599a9ea 100644 --- a/django_redis/client/default.py +++ b/django_redis/client/default.py @@ -772,3 +772,74 @@ def touch( # Convert to milliseconds timeout = int(timeout * 1000) return bool(client.pexpire(key, timeout)) + + def hdel( + self, + name: Any, + keys: List, + client: Optional[Redis] = None, + version: Optional[int] = None, + ) -> bool: + name = self.make_key(name, version=version) + if client is None: + client = self.get_client(write=True) + try: + return bool(client.hdel(name, *keys)) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + + def hset( + self, + name: Any, + key: Union[str, bytes], + value: Union[bytes, float, int, str], + version: Optional[int] = None, + mapping: Optional[dict] = None, + client: Optional[Redis] = None, + ) -> bool: + + original_client = client + tried = [] # type: List[int] + name = self.make_key(name, version=version) + value = self.encode(value) + while True: + try: + if client is None: + client, index = self.get_client( + write=True, tried=tried, show_index=True + ) + + return bool(client.hset(name, key, value, mapping)) + except _main_exceptions as e: + if ( + not original_client + and not self._replica_read_only + and len(tried) < len(self._server) + ): + tried.append(index) + client = None + continue + raise ConnectionInterrupted(connection=client) from e + + def hget( + self, + name: str, + key: str, + default=None, + version: Optional[int] = None, + client: Optional[Redis] = None, + ) -> Any: + if client is None: + client = self.get_client(write=False) + + name = self.make_key(name, version=version) + + try: + value = client.hget(name, key) + except _main_exceptions as e: + raise ConnectionInterrupted(connection=client) from e + + if value is None: + return default + + return self.decode(value) diff --git a/django_redis/client/sharded.py b/django_redis/client/sharded.py index a1b0edd0..7262bfb5 100644 --- a/django_redis/client/sharded.py +++ b/django_redis/client/sharded.py @@ -323,3 +323,38 @@ def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None, client=None): def clear(self, client=None): for connection in self._serverdict.values(): connection.flushdb() + + def hset( + self, name, key, value, timeout=DEFAULT_TIMEOUT, version=None, client=None + ): + """ + Persist a value to the cache, and set an optional expiration time. + """ + if client is None: + name = self.make_key(name, version=version) + client = self.get_server(name) + + return super().hset( + name=name, key=key, value=value, version=version, client=client + ) + + def hget(self, name, key, version=None, client=None): + """ + Persist a value to the cache, and set an optional expiration time. + """ + if client is None: + name = self.make_key(name, version=version) + client = self.get_server(name) + + return super().hget(name=name, key=key, version=version, client=client) + + def hdel(self, name, keys, version=None, client=None): + if client is None: + name = self.make_key(name, version=version) + client = self.get_server(name) + return super().hdel( + name=name, + keys=keys, + version=version, + client=client, + ) diff --git a/tests/test_backend.py b/tests/test_backend.py old mode 100644 new mode 100755 index 0e8e1fdf..c4e89c88 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -771,3 +771,18 @@ def test_clear(self, cache: RedisCache): cache.clear() value_from_cache_after_clear = cache.get("foo") assert value_from_cache_after_clear is None + + def test_hset_hget(self, cache: RedisCache): + cache.hset("foo", "bar", "baz") + cache.hset("foo", "baz", "bar") + value_from_cache = cache.hget("foo", "bar") + assert value_from_cache == "baz" + value_from_cache = cache.hget("foo", "baz") + assert value_from_cache == "bar" + + def test_hdel(self, cache: RedisCache): + cache.hset("foo", "bar", "baz") + cache.hset("foo", "baz", "bar") + cache.hdel("foo", ["bar", "baz"]) + assert cache.hget("foo", "bar") is None + assert cache.hget("foo", "baz") is None