{"id":7167,"library":"django-cache-memoize","title":"Django Cache Memoize","description":"django-cache-memoize is a Django utility that provides a memoization decorator, `cache_memoize`, which leverages the Django cache framework. It's designed to cache function call results, supports invalidation, and works with complex arguments and keyword arguments. The current version is 0.2.1, and it maintains an active release cadence.","status":"active","version":"0.2.1","language":"en","source_language":"en","source_url":"https://github.com/peterbe/django-cache-memoize","tags":["django","cache","memoization","decorator","performance"],"install":[{"cmd":"pip install django-cache-memoize","lang":"bash","label":"Install latest version"}],"dependencies":[{"reason":"Core framework dependency for cache integration.","package":"Django","optional":false}],"imports":[{"symbol":"cache_memoize","correct":"from cache_memoize import cache_memoize"}],"quickstart":{"code":"import random\nfrom django.conf import settings\nfrom django.core.cache import cache\nfrom django.http import HttpResponse\nfrom cache_memoize import cache_memoize\n\n# Minimal Django settings for cache configuration\nif not settings.configured:\n    settings.configure(\n        CACHES={\n            'default': {\n                'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',\n                'LOCATION': 'unique-snowflake',\n            }\n        }\n    )\n\n# Example of a function that would be expensive\n@cache_memoize(timeout=60 * 5) # Cache for 5 minutes\ndef expensive_calculation(a, b):\n    print(f\"Calculating for {a}, {b}...\") # This will only print on cache miss\n    return a + b + random.randint(0, 100)\n\n# Example Django view\ndef my_view(request):\n    result1 = expensive_calculation(10, 20)\n    result2 = expensive_calculation(10, 20) # This should be a cache hit\n    result3 = expensive_calculation(1, 2)\n\n    # Invalidate cache for specific arguments\n    # expensive_calculation.invalidate(10, 20)\n    # print(\"Invalidated (10, 20)\")\n    # result_after_invalidation = expensive_calculation(10, 20)\n\n    return HttpResponse(\n        f\"Result 1 (10, 20): {result1}<br>\" +\n        f\"Result 2 (10, 20) (cached): {result2}<br>\" +\n        f\"Result 3 (1, 2): {result3}<br>\" +\n        f\"Cache key for (10, 20): {expensive_calculation.get_memoize_key(10, 20)}\"\n    )\n\n# To run this, you would integrate it into a Django project's urls.py\n# e.g., path('my_cached_view/', my_view),\n# and ensure Django's cache settings are configured (as above or in settings.py)\n","lang":"python","description":"This quickstart demonstrates how to use the `cache_memoize` decorator on an expensive function within a minimal Django setup. It configures a local memory cache, applies the decorator with a timeout, and shows how subsequent calls with the same arguments retrieve cached results. It also illustrates how to manually invalidate a specific cached entry."},"warnings":[{"fix":"Ensure that the function returns only pickleable objects. For unpickleable items, consider caching only their serializable attributes or using Django's low-level cache API directly with custom serialization.","message":"The decorated function's return value must be pickleable by Python's `pickle` module. If the return value is not pickleable (e.g., certain unpicklable object instances, database connections), caching will fail.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Add a `CACHES` dictionary to your `settings.py` file, defining at least a 'default' cache backend (e.g., `django.core.cache.backends.locmem.LocMemCache`).","message":"To effectively use `django-cache-memoize`, Django's caching framework must be properly configured in your project's `settings.py` (e.g., using `LocMemCache`, `RedisCache`, or `MemcachedCache`). If no cache backend is configured, the decorator will not store or retrieve values.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Store or reconstruct the exact arguments used for caching if you need to invalidate specific entries. If broad invalidation is required, consider alternative strategies or custom cache key generation and management.","message":"Cache invalidation using `.invalidate(*args, **kwargs)` requires providing the exact same arguments (positional and keyword) that were used when the function was originally called. There is no built-in mechanism to invalidate cache entries based on partial argument matches or patterns.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Pass immutable objects (e.g., tuples, integers, strings) as arguments where possible. If mutable objects must be used, ensure they have a `__str__` method that produces a unique and consistent string representation for the object's identity relevant to caching.","message":"Using mutable objects (like lists or custom class instances without a stable `__str__` method) as arguments to a memoized function can lead to unexpected cache key generation or misses, as the default key generation relies on `str()` representation of arguments.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Modify the decorated function to return only basic Python types (numbers, strings, lists, dictionaries) or custom objects that are explicitly designed to be pickleable. For QuerySets, convert them to lists of model instances or dictionaries/values before returning.","cause":"The return value of your memoized function (or one of its components) is an unpickleable object, such as an active Django QuerySet, database connection, or a complex class instance not designed for pickling.","error":"TypeError: cannot pickle '_thread.RLock' object"},{"fix":"Verify your `CACHES` setting in `settings.py` is correctly configured and points to an available cache backend. Ensure any external cache servers (like Redis or Memcached) are running and accessible from your Django application. Check Django's `CACHE_MIDDLEWARE_SECONDS` if applicable, although this decorator uses low-level cache directly.","cause":"Django's cache framework is either not configured, misconfigured, or the cache backend (e.g., Memcached, Redis) is not running or accessible.","error":"My memoized function keeps executing on every call, it's not caching."},{"fix":"When calling `function.invalidate()`, provide the identical positional and keyword arguments as were passed during the original function call that created the cache entry. For example, `expensive_calculation.invalidate(10, 20)` matches `expensive_calculation(10, 20)`.","cause":"The arguments passed to `.invalidate()` do not exactly match the arguments originally used when the function's result was cached. `django-cache-memoize` generates cache keys based on the exact arguments.","error":"Cache is not clearing when I call .invalidate()"},{"fix":"After performing a database write operation that affects data potentially used by a memoized function, manually call `function.invalidate(*args, **kwargs)` for the relevant cache entry (or entries) to ensure fresh data is retrieved on subsequent calls.","cause":"The cache entry was not explicitly invalidated after the corresponding data in the database was modified. `django-cache-memoize` does not automatically detect database changes.","error":"Stale data is returned even after I update the database."}]}