Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/middleware/cache.py: 16%

81 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1""" 

2Cache middleware. If enabled, each Django-powered page will be cached based on 

3URL. The canonical way to enable cache middleware is to set 

4``UpdateCacheMiddleware`` as your first piece of middleware, and 

5``FetchFromCacheMiddleware`` as the last:: 

6 

7 MIDDLEWARE = [ 

8 'django.middleware.cache.UpdateCacheMiddleware', 

9 ... 

10 'django.middleware.cache.FetchFromCacheMiddleware' 

11 ] 

12 

13This is counter-intuitive, but correct: ``UpdateCacheMiddleware`` needs to run 

14last during the response phase, which processes middleware bottom-up; 

15``FetchFromCacheMiddleware`` needs to run last during the request phase, which 

16processes middleware top-down. 

17 

18The single-class ``CacheMiddleware`` can be used for some simple sites. 

19However, if any other piece of middleware needs to affect the cache key, you'll 

20need to use the two-part ``UpdateCacheMiddleware`` and 

21``FetchFromCacheMiddleware``. This'll most often happen when you're using 

22Django's ``LocaleMiddleware``. 

23 

24More details about how the caching works: 

25 

26* Only GET or HEAD-requests with status code 200 are cached. 

27 

28* The number of seconds each page is stored for is set by the "max-age" section 

29 of the response's "Cache-Control" header, falling back to the 

30 CACHE_MIDDLEWARE_SECONDS setting if the section was not found. 

31 

32* This middleware expects that a HEAD request is answered with the same response 

33 headers exactly like the corresponding GET request. 

34 

35* When a hit occurs, a shallow copy of the original response object is returned 

36 from process_request. 

37 

38* Pages will be cached based on the contents of the request headers listed in 

39 the response's "Vary" header. 

40 

41* This middleware also sets ETag, Last-Modified, Expires and Cache-Control 

42 headers on the response object. 

43 

44""" 

45 

46from django.conf import settings 

47from django.core.cache import DEFAULT_CACHE_ALIAS, caches 

48from django.utils.cache import ( 

49 get_cache_key, 

50 get_max_age, 

51 has_vary_header, 

52 learn_cache_key, 

53 patch_response_headers, 

54) 

55from django.utils.deprecation import MiddlewareMixin 

56 

57 

58class UpdateCacheMiddleware(MiddlewareMixin): 

59 """ 

60 Response-phase cache middleware that updates the cache if the response is 

61 cacheable. 

62 

63 Must be used as part of the two-part update/fetch cache middleware. 

64 UpdateCacheMiddleware must be the first piece of middleware in MIDDLEWARE 

65 so that it'll get called last during the response phase. 

66 """ 

67 

68 def __init__(self, get_response): 

69 super().__init__(get_response) 

70 self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS 

71 self.page_timeout = None 

72 self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX 

73 self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS 

74 self.cache = caches[self.cache_alias] 

75 

76 def _should_update_cache(self, request, response): 

77 return hasattr(request, "_cache_update_cache") and request._cache_update_cache 

78 

79 def process_response(self, request, response): 

80 """Set the cache, if needed.""" 

81 if not self._should_update_cache(request, response): 

82 # We don't need to update the cache, just return. 

83 return response 

84 

85 if response.streaming or response.status_code not in (200, 304): 

86 return response 

87 

88 # Don't cache responses that set a user-specific (and maybe security 

89 # sensitive) cookie in response to a cookie-less request. 

90 if ( 

91 not request.COOKIES 

92 and response.cookies 

93 and has_vary_header(response, "Cookie") 

94 ): 

95 return response 

96 

97 # Don't cache a response with 'Cache-Control: private' 

98 if "private" in response.get("Cache-Control", ()): 

99 return response 

100 

101 # Page timeout takes precedence over the "max-age" and the default 

102 # cache timeout. 

103 timeout = self.page_timeout 

104 if timeout is None: 

105 # The timeout from the "max-age" section of the "Cache-Control" 

106 # header takes precedence over the default cache timeout. 

107 timeout = get_max_age(response) 

108 if timeout is None: 

109 timeout = self.cache_timeout 

110 elif timeout == 0: 

111 # max-age was set to 0, don't cache. 

112 return response 

113 patch_response_headers(response, timeout) 

114 if timeout and response.status_code == 200: 

115 cache_key = learn_cache_key( 

116 request, response, timeout, self.key_prefix, cache=self.cache 

117 ) 

118 if hasattr(response, "render") and callable(response.render): 

119 response.add_post_render_callback( 

120 lambda r: self.cache.set(cache_key, r, timeout) 

121 ) 

122 else: 

123 self.cache.set(cache_key, response, timeout) 

124 return response 

125 

126 

127class FetchFromCacheMiddleware(MiddlewareMixin): 

128 """ 

129 Request-phase cache middleware that fetches a page from the cache. 

130 

131 Must be used as part of the two-part update/fetch cache middleware. 

132 FetchFromCacheMiddleware must be the last piece of middleware in MIDDLEWARE 

133 so that it'll get called last during the request phase. 

134 """ 

135 

136 def __init__(self, get_response): 

137 super().__init__(get_response) 

138 self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX 

139 self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS 

140 self.cache = caches[self.cache_alias] 

141 

142 def process_request(self, request): 

143 """ 

144 Check whether the page is already cached and return the cached 

145 version if available. 

146 """ 

147 if request.method not in ("GET", "HEAD"): 

148 request._cache_update_cache = False 

149 return None # Don't bother checking the cache. 

150 

151 # try and get the cached GET response 

152 cache_key = get_cache_key(request, self.key_prefix, "GET", cache=self.cache) 

153 if cache_key is None: 

154 request._cache_update_cache = True 

155 return None # No cache information available, need to rebuild. 

156 response = self.cache.get(cache_key) 

157 # if it wasn't found and we are looking for a HEAD, try looking just for that 

158 if response is None and request.method == "HEAD": 

159 cache_key = get_cache_key( 

160 request, self.key_prefix, "HEAD", cache=self.cache 

161 ) 

162 response = self.cache.get(cache_key) 

163 

164 if response is None: 

165 request._cache_update_cache = True 

166 return None # No cache information available, need to rebuild. 

167 

168 # hit, return cached response 

169 request._cache_update_cache = False 

170 return response 

171 

172 

173class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware): 

174 """ 

175 Cache middleware that provides basic behavior for many simple sites. 

176 

177 Also used as the hook point for the cache decorator, which is generated 

178 using the decorator-from-middleware utility. 

179 """ 

180 

181 def __init__(self, get_response, cache_timeout=None, page_timeout=None, **kwargs): 

182 super().__init__(get_response) 

183 # We need to differentiate between "provided, but using default value", 

184 # and "not provided". If the value is provided using a default, then 

185 # we fall back to system defaults. If it is not provided at all, 

186 # we need to use middleware defaults. 

187 

188 try: 

189 key_prefix = kwargs["key_prefix"] 

190 if key_prefix is None: 

191 key_prefix = "" 

192 self.key_prefix = key_prefix 

193 except KeyError: 

194 pass 

195 try: 

196 cache_alias = kwargs["cache_alias"] 

197 if cache_alias is None: 

198 cache_alias = DEFAULT_CACHE_ALIAS 

199 self.cache_alias = cache_alias 

200 self.cache = caches[self.cache_alias] 

201 except KeyError: 

202 pass 

203 

204 if cache_timeout is not None: 

205 self.cache_timeout = cache_timeout 

206 self.page_timeout = page_timeout