68 |
68 |
return func
|
69 |
69 |
|
70 |
70 |
|
71 |
|
def cache_and_validate(timeout, hashing=hashlib.md5):
|
72 |
|
'''
|
73 |
|
Decorator to add caching, with support for ETag and Last-modified
|
74 |
|
validation.
|
75 |
|
|
76 |
|
Just give it the time for caching.
|
77 |
|
'''
|
78 |
|
def transform(f):
|
79 |
|
f.cache = dict()
|
80 |
|
def get_content(request, *args, **kwargs):
|
81 |
|
'''
|
82 |
|
Content is kept as
|
83 |
|
|
84 |
|
(last_generation_time, last_modified_time, etag, content)
|
85 |
|
|
86 |
|
inside the f.cache dictionnary
|
87 |
|
'''
|
88 |
|
key=args+tuple(sorted(kwargs.items()))
|
89 |
|
if request.method == 'PURGE' and request.environ.get('REMOTE_ADDR') \
|
90 |
|
in settings.INTERNAL_IPS:
|
91 |
|
# purge the cache place
|
92 |
|
f.cache.pop(key, None)
|
93 |
|
now = dt.datetime.now()
|
94 |
|
if key in f.cache:
|
95 |
|
date, last_modified, etag, mime_type, old_content = f.cache[key]
|
96 |
|
if callable(timeout):
|
97 |
|
real_timeout = timeout()
|
98 |
|
else:
|
99 |
|
real_timeout = timeout
|
100 |
|
if now - date < dt.timedelta(seconds=real_timeout):
|
101 |
|
return date, last_modified, etag, mime_type, old_content
|
102 |
|
else:
|
103 |
|
content = f(request, *args, **kwargs)
|
104 |
|
if old_content == content.content:
|
105 |
|
data = (now, last_modified, etag, mime_type, old_content)
|
106 |
|
return data
|
107 |
|
else:
|
108 |
|
content = f(request, *args, **kwargs)
|
109 |
|
if content.status_code == 200:
|
110 |
|
content_type = content.get('Content-Type', None)
|
111 |
|
data = now, now, hashing(content.content).hexdigest(), content_type, content.content
|
112 |
|
f.cache[key] = data
|
113 |
|
else:
|
114 |
|
data = None, None, None, None, content
|
115 |
|
return data
|
116 |
|
def get_last_modified(request, *args, **kwargs):
|
117 |
|
_, last_modified, _, _, _ = get_content(request, *args, **kwargs)
|
118 |
|
return last_modified
|
119 |
|
def get_etag(request, *args, **kwargs):
|
120 |
|
_, _, etag, _, _ = get_content(request, *args, **kwargs)
|
121 |
|
return etag
|
122 |
|
@condition(etag_func=get_etag, last_modified_func=get_last_modified)
|
123 |
|
def replacement(request, *args, **kwargs):
|
124 |
|
_, _, _, content_type, content = get_content(request, *args, **kwargs)
|
125 |
|
if isinstance(content, six.string_types):
|
126 |
|
return HttpResponse(content, content_type=content_type)
|
127 |
|
else:
|
128 |
|
return content
|
129 |
|
return replacement
|
130 |
|
return transform
|
131 |
|
|
132 |
71 |
def import_from(module, name):
|
133 |
72 |
module = __import__(module, fromlist=[name])
|
134 |
73 |
return getattr(module, name)
|
135 |
|
-
|