Commit 6fe22b30 authored by zedr's avatar zedr Committed by Baptiste Mispelon
Browse files

Fixed #22085 -- Added a feature for setting non-expiring keys as the default.

This feature allows the default `TIMEOUT` Cache argument to be set to `None`,
so that cache instances can set a non-expiring key as the default,
instead of using the default value of 5 minutes.

Previously, this was possible only by passing `None` as an argument to
the set() method of objects of type `BaseCache` (and subtypes).
parent 24f0113f
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ def get_key_func(key_func):
class BaseCache(object):
    def __init__(self, params):
        timeout = params.get('timeout', params.get('TIMEOUT', 300))
        if timeout is not None:
            try:
                timeout = int(timeout)
            except (ValueError, TypeError):
+4 −0
Original line number Diff line number Diff line
@@ -255,6 +255,10 @@ Default: 300

The number of seconds before a cache entry is considered stale.

.. versionadded:: 1.7

If the value of this settings is ``None``, cache entries will not expire.

.. setting:: CACHES-VERSION

VERSION
+5 −0
Original line number Diff line number Diff line
@@ -441,6 +441,11 @@ Cache
  thread-safe any more, as :data:`django.core.cache.caches` now yields
  different instances per thread.

* Defining the :setting:`TIMEOUT <CACHES-TIMEOUT>` argument of the
  :setting:`CACHES` setting as ``None`` will set the cache keys as
  "non-expiring" by default. Previously, it was only possible to pass
  ``timeout=None` to the cache backend's ``set()`` method.

Email
^^^^^

+7 −2
Original line number Diff line number Diff line
@@ -363,9 +363,14 @@ Each cache backend can be given additional arguments to control caching
behavior. These arguments are provided as additional keys in the
:setting:`CACHES` setting. Valid arguments are as follows:


* :setting:`TIMEOUT <CACHES-TIMEOUT>`: The default timeout, in
  seconds, to use for the cache. This argument defaults to ``300``
  seconds (5 minutes).
  seconds, to use for the cache. This argument defaults to ``300`` seconds (5 minutes).

  .. versionadded:: 1.7

  You can set ``TIMEOUT`` to ``None`` so that, by default, cache keys never
  expire.

* :setting:`OPTIONS <CACHES-OPTIONS>`: Any options that should be
  passed to the cache backend. The list of valid options will vary
+80 −2
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ from __future__ import unicode_literals

import os
import re
import copy
import shutil
import tempfile
import threading
@@ -15,7 +16,8 @@ import warnings

from django.conf import settings
from django.core import management
from django.core.cache import cache, caches, CacheKeyWarning, InvalidCacheBackendError
from django.core.cache import (cache, caches, CacheKeyWarning,
    InvalidCacheBackendError, DEFAULT_CACHE_ALIAS)
from django.db import connection, router, transaction
from django.core.cache.utils import make_template_fragment_key
from django.http import HttpResponse, StreamingHttpResponse
@@ -1175,7 +1177,7 @@ class CustomCacheKeyValidationTests(TestCase):
class GetCacheTests(IgnorePendingDeprecationWarningsMixin, TestCase):

    def test_simple(self):
        from django.core.cache import caches, DEFAULT_CACHE_ALIAS, get_cache
        from django.core.cache import caches, get_cache
        self.assertIsInstance(
            caches[DEFAULT_CACHE_ALIAS],
            get_cache('default').__class__
@@ -1204,6 +1206,82 @@ class GetCacheTests(IgnorePendingDeprecationWarningsMixin, TestCase):
        self.assertTrue(cache.closed)


DEFAULT_MEMORY_CACHES_SETTINGS = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'unique-snowflake',
    }
}
NEVER_EXPIRING_CACHES_SETTINGS = copy.deepcopy(DEFAULT_MEMORY_CACHES_SETTINGS)
NEVER_EXPIRING_CACHES_SETTINGS['default']['TIMEOUT'] = None


class DefaultNonExpiringCacheKeyTests(TestCase):
    """Tests that verify that settings having Cache arguments with a TIMEOUT
    set to `None` will create Caches that will set non-expiring keys.

    This fixes ticket #22085.
    """
    def setUp(self):
        # The 5 minute (300 seconds) default expiration time for keys is
        # defined in the implementation of the initializer method of the
        # BaseCache type.
        self.DEFAULT_TIMEOUT = caches[DEFAULT_CACHE_ALIAS].default_timeout

    def tearDown(self):
        del(self.DEFAULT_TIMEOUT)

    def test_default_expiration_time_for_keys_is_5_minutes(self):
        """The default expiration time of a cache key is 5 minutes.

        This value is defined inside the __init__() method of the
        :class:`django.core.cache.backends.base.BaseCache` type.
        """
        self.assertEquals(300, self.DEFAULT_TIMEOUT)

    def test_caches_with_unset_timeout_has_correct_default_timeout(self):
        """Caches that have the TIMEOUT parameter undefined in the default
        settings will use the default 5 minute timeout.
        """
        cache = caches[DEFAULT_CACHE_ALIAS]
        self.assertEquals(self.DEFAULT_TIMEOUT, cache.default_timeout)

    @override_settings(CACHES=NEVER_EXPIRING_CACHES_SETTINGS)
    def test_caches_set_with_timeout_as_none_has_correct_default_timeout(self):
        """Memory caches that have the TIMEOUT parameter set to `None` in the
        default settings with have `None` as the default timeout.

        This means "no timeout".
        """
        cache = caches[DEFAULT_CACHE_ALIAS]
        self.assertIs(None, cache.default_timeout)
        self.assertEquals(None, cache.get_backend_timeout())

    @override_settings(CACHES=DEFAULT_MEMORY_CACHES_SETTINGS)
    def test_caches_with_unset_timeout_set_expiring_key(self):
        """Memory caches that have the TIMEOUT parameter unset will set cache
        keys having the default 5 minute timeout.
        """
        key = "my-key"
        value = "my-value"
        cache = caches[DEFAULT_CACHE_ALIAS]
        cache.set(key, value)
        cache_key = cache.make_key(key)
        self.assertNotEquals(None, cache._expire_info[cache_key])

    @override_settings(CACHES=NEVER_EXPIRING_CACHES_SETTINGS)
    def text_caches_set_with_timeout_as_none_set_non_expiring_key(self):
        """Memory caches that have the TIMEOUT parameter set to `None` will set
        a non expiring key by default.
        """
        key = "another-key"
        value = "another-value"
        cache = caches[DEFAULT_CACHE_ALIAS]
        cache.set(key, value)
        cache_key = cache.make_key(key)
        self.assertEquals(None, cache._expire_info[cache_key])


@override_settings(
    CACHE_MIDDLEWARE_KEY_PREFIX='settingsprefix',
    CACHE_MIDDLEWARE_SECONDS=1,