1
1
from functools import wraps
2
2
import itertools
3
+ import json
4
+ import inspect
3
5
4
6
import hashlib
5
7
from urllib .parse import quote
6
8
9
+ from django .db import models
7
10
from django .core .cache import caches , DEFAULT_CACHE_ALIAS
8
11
9
12
from django .utils .encoding import force_bytes
14
17
def cache_memoize (
15
18
timeout ,
16
19
prefix = None ,
20
+ extra = None ,
17
21
args_rewrite = None ,
18
22
hit_callable = None ,
19
23
miss_callable = None ,
@@ -27,6 +31,8 @@ def cache_memoize(
27
31
28
32
:arg int timeout: Number of seconds to store the result if not None
29
33
:arg string prefix: If None becomes the function name.
34
+ :arg extra: Optional callable or serializable structure of key
35
+ components cache should vary on.
30
36
:arg function args_rewrite: Callable that rewrites the args first useful
31
37
if your function needs nontrivial types but you know a simple way to
32
38
re-represent them for the sake of the cache key.
@@ -88,6 +94,17 @@ def callmeonce(arg1):
88
94
callmeonce('peter') # nothing printed
89
95
callmeonce('peter', _refresh=True) # will print 'peter'
90
96
97
+ If your cache depends on external state you can provide `extra` values::
98
+
99
+ @cache_memoize(100, extra={'version': 2})
100
+ def callmeonce(arg1):
101
+ print(arg1)
102
+
103
+ An `extra` argument can be callable or any serializable structure::
104
+
105
+ @cache_memoize(100, extra=lambda req: req.user.is_staff)
106
+ def callmeonce(arg1):
107
+ print(arg1)
91
108
"""
92
109
93
110
if args_rewrite is None :
@@ -97,6 +114,17 @@ def noop(*args):
97
114
98
115
args_rewrite = noop
99
116
117
+ def obj_key (obj ):
118
+ if isinstance (obj , models .Model ):
119
+ return "%s.%s.%s" % (obj ._meta .app_label , obj ._meta .model_name , obj .pk )
120
+ elif hasattr (obj , "build_absolute_uri" ):
121
+ return obj .build_absolute_uri ()
122
+ elif inspect .isfunction (obj ):
123
+ factors = [obj .__module__ , obj .__name__ ]
124
+ return factors
125
+ else :
126
+ return str (obj )
127
+
100
128
def decorator (func ):
101
129
def _default_make_cache_key (* args , ** kwargs ):
102
130
cache_key = ":" .join (
@@ -109,8 +137,13 @@ def _default_make_cache_key(*args, **kwargs):
109
137
)
110
138
)
111
139
prefix_ = prefix or "." .join ((func .__module__ or "" , func .__qualname__ ))
140
+ extra_val = json .dumps (
141
+ extra (* args , ** kwargs ) if callable (extra ) else extra ,
142
+ sort_keys = True ,
143
+ default = obj_key ,
144
+ )
112
145
return hashlib .md5 (
113
- force_bytes ("cache_memoize" + prefix_ + cache_key )
146
+ force_bytes ("cache_memoize" + prefix_ + cache_key + extra_val )
114
147
).hexdigest ()
115
148
116
149
_make_cache_key = key_generator_callable or _default_make_cache_key
0 commit comments