diff --git a/django/utils/cache.py b/django/utils/cache.py index 90292ce4da60..5a5df2d9de84 100644 --- a/django/utils/cache.py +++ b/django/utils/cache.py @@ -23,6 +23,7 @@ from django.core.cache import caches from django.http import HttpResponse, HttpResponseNotModified from django.utils.crypto import md5 +from django.utils.encoding import iri_to_uri from django.utils.http import http_date, parse_etags, parse_http_date_safe, quote_etag from django.utils.log import log_response from django.utils.regex_helper import _lazy_re_compile @@ -333,20 +334,35 @@ def has_vary_header(response, header_query): return header_query.lower() in existing_headers -def _i18n_cache_key_suffix(request, cache_key): - """If necessary, add the current locale or time zone to the cache key.""" +def _i18n_cache_key_suffix(request, cache_key:str) -> str: + """ + If necessary, add the current locale or time zone to the cache key. + """ + language_code = "" + timezone = "" + if settings.USE_I18N: # first check if LocaleMiddleware or another middleware added # LANGUAGE_CODE to request, then fall back to the active language # which in turn can also fall back to settings.LANGUAGE_CODE - cache_key += ".%s" % getattr(request, "LANGUAGE_CODE", get_language()) + language_code = getattr(request, "LANGUAGE_CODE", get_language()) if settings.USE_TZ: - cache_key += ".%s" % get_current_timezone_name() - return cache_key + timezone = get_current_timezone_name() + + return cache_key.handle_cache_key_suffixes( + cache_key=cache_key, language_code=language_code, timezone=timezone + ) + + + def _generate_cache_key(request, method, headerlist, key_prefix): - """Return a cache key from the headers given in the header list.""" + """ + Entry point for cache key generation. + + Return a cache key from the headers given in the header list. + """ ctx = md5(usedforsecurity=False) for header in headerlist: value = request.META.get(header) @@ -359,19 +375,32 @@ def _generate_cache_key(request, method, headerlist, key_prefix): url.hexdigest(), ctx.hexdigest(), ) + return _i18n_cache_key_suffix(request, cache_key) -def _generate_cache_header_key(key_prefix, request): - """Return a cache key for the header cache.""" - url = md5(request.build_absolute_uri().encode("ascii"), usedforsecurity=False) +def _generate_cache_key_from_request(*, key_prefix, request): + uri = _get_uri_from_request(request) + url_hash = md5(uri, usedforsecurity=False) cache_key = "views.decorators.cache.cache_header.%s.%s" % ( key_prefix, - url.hexdigest(), + url_hash.hexdigest(), ) return _i18n_cache_key_suffix(request, cache_key) +def _generate_cache_key_from_uri(*, key_prefix, uri): + + url_hash = md5(uri, usedforsecurity=False) + cache_key = "views.decorators.cache.cache_header.%s.%s" % ( + key_prefix, + url_hash.hexdigest(), + ) + return _i18n_cache_key_suffix(cache_key) + + + + def get_cache_key(request, key_prefix=None, method="GET", cache=None): """ Return a cache key based on the request URL and query. It can be used @@ -411,6 +440,8 @@ def learn_cache_key(request, response, cache_timeout=None, key_prefix=None, cach key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX if cache_timeout is None: cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS + + # Generate the cache key. cache_key = _generate_cache_header_key(key_prefix, request) if cache is None: cache = caches[settings.CACHE_MIDDLEWARE_ALIAS] @@ -426,6 +457,8 @@ def learn_cache_key(request, response, cache_timeout=None, key_prefix=None, cach if header != "ACCEPT_LANGUAGE" or not is_accept_language_redundant: headerlist.append("HTTP_" + header) headerlist.sort() + + # Set the cache key. cache.set(cache_key, headerlist, cache_timeout) return _generate_cache_key(request, request.method, headerlist, key_prefix) else: