Skip to content

Commit f669b9d

Browse files
committed
Issue #598 extend support for the hashmap functions
1 parent e11150a commit f669b9d

File tree

4 files changed

+281
-12
lines changed

4 files changed

+281
-12
lines changed

django_redis/cache.py

+20
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,22 @@ def sunionstore(self, *args, **kwargs):
257257
def hset(self, *args, **kwargs):
258258
return self.client.hset(*args, **kwargs)
259259

260+
@omit_exception
261+
def hsetnx(self, *args, **kwargs):
262+
return self.client.hsetnx(*args, **kwargs)
263+
264+
@omit_exception
265+
def hget(self, *args, **kwargs):
266+
return self.client.hget(*args, **kwargs)
267+
268+
@omit_exception
269+
def hgetall(self, *args, **kwargs):
270+
return self.client.hgetall(*args, **kwargs)
271+
272+
@omit_exception
273+
def hmget(self, *args, **kwargs):
274+
return self.client.hmget(*args, **kwargs)
275+
260276
@omit_exception
261277
def hdel(self, *args, **kwargs):
262278
return self.client.hdel(*args, **kwargs)
@@ -272,3 +288,7 @@ def hkeys(self, *args, **kwargs):
272288
@omit_exception
273289
def hexists(self, *args, **kwargs):
274290
return self.client.hexists(*args, **kwargs)
291+
292+
@omit_exception
293+
def hincrby(self, *args, **kwargs):
294+
return self.client.hincrby(*args, **kwargs)

django_redis/client/default.py

+91-2
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ def _incr(
621621
# if cached value or total value is greater than 64 bit signed
622622
# integer.
623623
# elif int is encoded. so redis sees the data as string.
624-
# In this situations redis will throw ResponseError
624+
# In these situations redis will throw ResponseError
625625

626626
# try to keep TTL of key
627627
timeout = self.ttl(key, version=version, client=client)
@@ -1115,14 +1115,77 @@ def hset(
11151115
"""
11161116
if client is None:
11171117
client = self.get_client(write=True)
1118+
name = self.make_key(name, version=version)
11181119
nkey = self.make_key(key, version=version)
11191120
nvalue = self.encode(value)
11201121
return int(client.hset(name, nkey, nvalue))
11211122

1122-
def hdel(
1123+
def hsetnx(
11231124
self,
11241125
name: str,
11251126
key: KeyT,
1127+
value: EncodableT,
1128+
version: Optional[int] = None,
1129+
client: Optional[Redis] = None,
1130+
) -> int:
1131+
if client is None:
1132+
client = self.get_client(write=True)
1133+
nkey = self.make_key(key, version=version)
1134+
name = self.make_key(name, version=version)
1135+
nvalue = self.encode(value)
1136+
return int(client.hsetnx(name, nkey, nvalue))
1137+
1138+
def hget(
1139+
self,
1140+
name: KeyT,
1141+
key: KeyT,
1142+
version: Optional[int] = None,
1143+
client: Optional[Redis] = None,
1144+
) -> Any:
1145+
"""
1146+
Return the value of hash name at key.
1147+
"""
1148+
if client is None:
1149+
client = self.get_client(write=False)
1150+
name = self.make_key(name, version=version)
1151+
nkey = self.make_key(key, version=version)
1152+
value = client.hget(name, nkey)
1153+
if value is None:
1154+
return None
1155+
return self.decode(value)
1156+
1157+
def hgetall(
1158+
self,
1159+
name: KeyT,
1160+
version: Optional[int] = None,
1161+
client: Optional[Redis] = None,
1162+
) -> Dict[str, Any]:
1163+
1164+
if client is None:
1165+
client = self.get_client(write=False)
1166+
name = self.make_key(name, version=version)
1167+
data = client.hgetall(name)
1168+
return {self.reverse_key(k.decode()): self.decode(v) for k, v in data.items()}
1169+
1170+
def hmget(
1171+
self,
1172+
name: KeyT,
1173+
*keys: KeyT,
1174+
version: Optional[int] = None,
1175+
client: Optional[Redis] = None,
1176+
) -> List[Any]:
1177+
if client is None:
1178+
client = self.get_client(write=False)
1179+
name = self.make_key(name, version=version)
1180+
nkeys = [self.make_key(k, version=version) for k in keys]
1181+
return [
1182+
self.decode(v) if v is not None else None for v in client.hmget(name, nkeys)
1183+
]
1184+
1185+
def hdel(
1186+
self,
1187+
name: KeyT,
1188+
key: KeyT,
11261189
version: Optional[int] = None,
11271190
client: Optional[Redis] = None,
11281191
) -> int:
@@ -1132,29 +1195,34 @@ def hdel(
11321195
"""
11331196
if client is None:
11341197
client = self.get_client(write=True)
1198+
name = self.make_key(name, version=version)
11351199
nkey = self.make_key(key, version=version)
11361200
return int(client.hdel(name, nkey))
11371201

11381202
def hlen(
11391203
self,
11401204
name: str,
1205+
version: Optional[int] = None,
11411206
client: Optional[Redis] = None,
11421207
) -> int:
11431208
"""
11441209
Return the number of items in hash name.
11451210
"""
11461211
if client is None:
11471212
client = self.get_client(write=False)
1213+
name = self.make_key(name, version=version)
11481214
return int(client.hlen(name))
11491215

11501216
def hkeys(
11511217
self,
11521218
name: str,
1219+
version: Optional[int] = None,
11531220
client: Optional[Redis] = None,
11541221
) -> List[Any]:
11551222
"""
11561223
Return a list of keys in hash name.
11571224
"""
1225+
name = self.make_key(name, version=version)
11581226
if client is None:
11591227
client = self.get_client(write=False)
11601228
try:
@@ -1174,5 +1242,26 @@ def hexists(
11741242
"""
11751243
if client is None:
11761244
client = self.get_client(write=False)
1245+
name = self.make_key(name, version=version)
11771246
nkey = self.make_key(key, version=version)
11781247
return bool(client.hexists(name, nkey))
1248+
1249+
def hincrby(
1250+
self,
1251+
name: KeyT,
1252+
key: KeyT,
1253+
increment: int = 1,
1254+
version: Optional[int] = None,
1255+
client: Optional[Redis] = None,
1256+
) -> int:
1257+
if client is None:
1258+
client = self.get_client(write=True)
1259+
name = self.make_key(name, version=version)
1260+
nkey = self.make_key(key, version=version)
1261+
try:
1262+
value = client.hincrby(name, nkey, increment)
1263+
except ResponseError as exc:
1264+
value = self.hget(name, nkey)
1265+
msg = f"Value: {value} is not an integer or out of range."
1266+
raise ValueError(msg) from exc
1267+
return int(value)

django_redis/client/sharded.py

+122
Original file line numberDiff line numberDiff line change
@@ -483,3 +483,125 @@ def smismember(
483483
key = self.make_key(key, version=version)
484484
client = self.get_server(key)
485485
return super().smismember(key, *members, version=version, client=client)
486+
487+
def hset(
488+
self,
489+
name: str,
490+
key: KeyT,
491+
value: Any,
492+
version: Optional[int] = None,
493+
client: Optional[Redis] = None,
494+
) -> int:
495+
if client is None:
496+
name = self.make_key(name, version=version)
497+
client = self.get_server(name)
498+
return super().hset(name, key, value, version=version, client=client)
499+
500+
def hsetnx(
501+
self,
502+
name: str,
503+
key: KeyT,
504+
value: Any,
505+
version: Optional[int] = None,
506+
client: Optional[Redis] = None,
507+
) -> int:
508+
if client is None:
509+
name = self.make_key(name, version=version)
510+
client = self.get_server(name)
511+
return super().hsetnx(name, key, value, version=version, client=client)
512+
513+
def hlen(
514+
self,
515+
name: str,
516+
version: Optional[int] = None,
517+
client: Optional[Redis] = None,
518+
) -> int:
519+
if client is None:
520+
name = self.make_key(name, version=version)
521+
client = self.get_server(name)
522+
return super().hlen(name, version=version, client=client)
523+
524+
def hget(
525+
self,
526+
name: str,
527+
key: KeyT,
528+
version: Optional[int] = None,
529+
client: Optional[Redis] = None,
530+
) -> Any:
531+
if client is None:
532+
name = self.make_key(name, version=version)
533+
client = self.get_server(name)
534+
return super().hget(name, key, version=version, client=client)
535+
536+
def hexists(
537+
self,
538+
name: str,
539+
key: KeyT,
540+
version: Optional[int] = None,
541+
client: Optional[Redis] = None,
542+
) -> bool:
543+
if client is None:
544+
name = self.make_key(name, version=version)
545+
client = self.get_server(name)
546+
return super().hexists(name, key, version=version, client=client)
547+
548+
def hkeys(
549+
self,
550+
name: str,
551+
version: Optional[int] = None,
552+
client: Optional[Redis] = None,
553+
) -> List[Any]:
554+
if client is None:
555+
name = self.make_key(name, version=version)
556+
client = self.get_server(name)
557+
return super().hkeys(name, client=client)
558+
559+
def hgetall(
560+
self,
561+
name: str,
562+
version: Optional[int] = None,
563+
client: Optional[Redis] = None,
564+
) -> Any:
565+
if client is None:
566+
name = self.make_key(name, version=version)
567+
client = self.get_server(name)
568+
return super().hgetall(name, version=version, client=client)
569+
570+
def hincrby(
571+
self,
572+
name: KeyT,
573+
key: KeyT,
574+
increment: int = 1,
575+
version: Optional[int] = None,
576+
client: Optional[Redis] = None,
577+
) -> int:
578+
if client is None:
579+
name = self.make_key(name, version=version)
580+
client = self.get_server(name)
581+
return super().hincrby(
582+
name, key, increment=increment, version=version, client=client
583+
)
584+
585+
def hmget(
586+
self,
587+
name: KeyT,
588+
*keys: KeyT,
589+
version: Optional[int] = None,
590+
client: Optional[Redis] = None,
591+
) -> List[Any]:
592+
if client is None:
593+
name = self.make_key(name, version=version)
594+
client = self.get_server(name)
595+
return super().hmget(name, *keys, version=version, client=client)
596+
597+
def hdel(
598+
self,
599+
name: KeyT,
600+
key: KeyT,
601+
version: Optional[int] = None,
602+
client: Optional[Redis] = None,
603+
) -> int:
604+
if client is None:
605+
name = self.make_key(name, version=version)
606+
client = self.get_server(name)
607+
return super().hdel(name, key, version=version, client=client)

tests/test_backend.py

+48-10
Original file line numberDiff line numberDiff line change
@@ -810,17 +810,61 @@ def test_clear(self, cache: RedisCache):
810810
assert value_from_cache_after_clear is None
811811

812812
def test_hset(self, cache: RedisCache):
813-
if isinstance(cache.client, ShardClient):
814-
pytest.skip("ShardClient doesn't support get_client")
815813
cache.hset("foo_hash1", "foo1", "bar1")
816814
cache.hset("foo_hash1", "foo2", "bar2")
817815
assert cache.hlen("foo_hash1") == 2
818816
assert cache.hexists("foo_hash1", "foo1")
819817
assert cache.hexists("foo_hash1", "foo2")
820818

819+
def test_hsetnx(self, cache: RedisCache):
820+
result_foo1 = cache.hsetnx("foo_hash1", "foo1", "bar1")
821+
result_foo2 = cache.hsetnx("foo_hash1", "foo2", "bar2")
822+
result_foo2_1 = cache.hsetnx("foo_hash1", "foo2", "bar2")
823+
assert result_foo1 == 1
824+
assert result_foo2 == 1
825+
assert result_foo2_1 == 0
826+
assert cache.hlen("foo_hash1") == 2
827+
assert cache.hexists("foo_hash1", "foo1")
828+
assert cache.hexists("foo_hash1", "foo2")
829+
830+
def test_hget(self, cache: RedisCache):
831+
cache.hset("foo_hash1", "foo1", "bar1")
832+
cache.hset("foo_hash1", "foo2", "bar2")
833+
assert cache.hget("foo_hash1", "foo1") == "bar1"
834+
assert cache.hget("foo_hash1", "foo2") == "bar2"
835+
836+
def test_hgetall(self, cache: RedisCache):
837+
cache.hset("foo_hash1", "foo1", "bar1")
838+
cache.hset("foo_hash1", "foo2", "bar2")
839+
assert cache.hgetall("foo_hash1") == {
840+
"foo1": "bar1",
841+
"foo2": "bar2",
842+
}
843+
844+
def test_hmget(self, cache: RedisCache):
845+
cache.hset("foo_hash1", "foo1", "bar1")
846+
cache.hset("foo_hash1", "foo2", "bar2")
847+
cache.hset("foo_hash1", "foo3", "bar3")
848+
assert cache.hmget("foo_hash1", "foo3", "foo1") == ["bar3", "bar1"]
849+
assert cache.hmget("foo_hash1", "foo1", "foo2", "not-a-key") == [
850+
"bar1",
851+
"bar2",
852+
None,
853+
]
854+
855+
def test_hincrby(self, cache: RedisCache):
856+
cache.hset("foo_hash1", "foo1", 1)
857+
assert cache.hincrby("foo_hash1", "foo1") == 2
858+
859+
assert cache.hincrby("foo_hash1", "foo2", 3) == 3
860+
assert cache.hincrby("foo_hash1", "foo2", -3) == 0
861+
862+
cache.hset("foo_hash1", "foo3", "not_an_int")
863+
864+
with pytest.raises(ValueError):
865+
cache.hincrby("foo_hash1", "foo3", -3)
866+
821867
def test_hdel(self, cache: RedisCache):
822-
if isinstance(cache.client, ShardClient):
823-
pytest.skip("ShardClient doesn't support get_client")
824868
cache.hset("foo_hash2", "foo1", "bar1")
825869
cache.hset("foo_hash2", "foo2", "bar2")
826870
assert cache.hlen("foo_hash2") == 2
@@ -831,17 +875,13 @@ def test_hdel(self, cache: RedisCache):
831875
assert cache.hexists("foo_hash2", "foo2")
832876

833877
def test_hlen(self, cache: RedisCache):
834-
if isinstance(cache.client, ShardClient):
835-
pytest.skip("ShardClient doesn't support get_client")
836878
assert cache.hlen("foo_hash3") == 0
837879
cache.hset("foo_hash3", "foo1", "bar1")
838880
assert cache.hlen("foo_hash3") == 1
839881
cache.hset("foo_hash3", "foo2", "bar2")
840882
assert cache.hlen("foo_hash3") == 2
841883

842884
def test_hkeys(self, cache: RedisCache):
843-
if isinstance(cache.client, ShardClient):
844-
pytest.skip("ShardClient doesn't support get_client")
845885
cache.hset("foo_hash4", "foo1", "bar1")
846886
cache.hset("foo_hash4", "foo2", "bar2")
847887
cache.hset("foo_hash4", "foo3", "bar3")
@@ -851,8 +891,6 @@ def test_hkeys(self, cache: RedisCache):
851891
assert keys[i] == f"foo{i + 1}"
852892

853893
def test_hexists(self, cache: RedisCache):
854-
if isinstance(cache.client, ShardClient):
855-
pytest.skip("ShardClient doesn't support get_client")
856894
cache.hset("foo_hash5", "foo1", "bar1")
857895
assert cache.hexists("foo_hash5", "foo1")
858896
assert not cache.hexists("foo_hash5", "foo")

0 commit comments

Comments
 (0)