{"id":8090,"library":"django-cachalot","title":"django-cachalot","description":"django-cachalot is a Django package that automatically caches all Django ORM queries and transparently invalidates them when data changes. It aims to provide significant performance improvements by reducing database hits without requiring developers to write explicit caching logic. The library is actively maintained, with version 2.9.0 supporting recent Django releases and Python versions, and new releases frequently adapting to Django's evolution.","status":"active","version":"2.9.0","language":"en","source_language":"en","source_url":"https://github.com/noripyt/django-cachalot","tags":["django","cache","orm","performance","database"],"install":[{"cmd":"pip install django-cachalot","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Core dependency for a Django application. Currently supports Django 4.2, 5.2, and 6.0.","package":"Django","optional":false},{"reason":"Common and recommended cache backend for production use.","package":"django-redis","optional":true},{"reason":"Alternative Memcached cache backend.","package":"python-memcached","optional":true},{"reason":"Faster Memcached cache backend, typically used with Django >= 1.7.","package":"pylibmc","optional":true}],"imports":[{"note":"The primary 'import' for django-cachalot is adding it to INSTALLED_APPS in your Django settings.","symbol":"cachalot","correct":"INSTALLED_APPS = [\n    # ...\n    'cachalot',\n]"},{"note":"Access programmatically via `cachalot_settings` from the top-level `cachalot` package, not `cachalot.settings` directly.","wrong":"from cachalot.settings import cachalot_settings","symbol":"cachalot_settings","correct":"from cachalot import cachalot_settings"}],"quickstart":{"code":"import os\n\n# settings.py\n\nSECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'a-very-secret-key')\n\nINSTALLED_APPS = [\n    # ... other apps\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n    'cachalot',\n]\n\n# Configure a cache backend (e.g., Redis)\nCACHES = {\n    'default': {\n        'BACKEND': 'django_redis.cache.RedisCache',\n        'LOCATION': os.environ.get('REDIS_URL', 'redis://127.0.0.1:6379/1'),\n        'OPTIONS': {\n            'CLIENT_CLASS': 'django_redis.client.DefaultClient',\n            'IGNORE_EXCEPTIONS': True # Recommended for graceful degradation if cache is down\n        }\n    }\n}\n\nDATABASES = {\n    'default': {\n        'ENGINE': 'django.db.backends.postgresql',\n        'NAME': 'mydatabase',\n        'USER': 'mydatabaseuser',\n        'PASSWORD': 'password',\n        'HOST': '127.0.0.1',\n        'PORT': '5432',\n    }\n}\n\n# To verify caching, you can add CachalotPanel to Django Debug Toolbar\nDEBUG_TOOLBAR_PANELS = [\n    # ... other panels\n    'cachalot.panels.CachalotPanel',\n]","lang":"python","description":"To enable django-cachalot, add 'cachalot' to your `INSTALLED_APPS` and ensure you have a Django cache backend configured in your `CACHES` setting. For production, a shared cache like Redis or Memcached is recommended. For local development, `LocMemCache` can be used, but be aware of its limitations in multi-process environments. It's often beneficial to configure `IGNORE_EXCEPTIONS` for your cache backend to prevent application crashes if the cache service is unavailable."},"warnings":[{"fix":"Upgrade your Django project to a compatible version or use an older django-cachalot release that supports your Django version.","message":"As of v2.9.0, Django 3.2 support has been dropped. Ensure your Django project is running a supported version (Django 4.2, 5.2, or 6.0).","severity":"breaking","affected_versions":">=2.9.0"},{"fix":"Upgrade your Django project to a compatible version or use an older django-cachalot release.","message":"With v2.6.0, support for Django 2.2 and 4.0 was dropped. Projects using these versions must stick to django-cachalot <2.6.0.","severity":"breaking","affected_versions":">=2.6.0, <2.9.0"},{"fix":"Evaluate if django-cachalot's per-table invalidation strategy aligns with your application's write patterns. For very high-write tables, consider alternative caching strategies (e.g., per-object caching with other libraries like `django-cacheops` or `django-cache-machine` for hot data, or manual caching for specific querysets) or optimize database queries directly.","message":"django-cachalot caches results on a per-table basis, not per-object. If a single object in a table is modified, all cached queries related to that entire table are invalidated. This can lead to frequent cache invalidations and potential performance degradation if tables experience a high rate of modifications (e.g., >50 modifications per minute).","severity":"gotcha","affected_versions":"All"},{"fix":"If caching is critical for raw SQL queries, implement manual caching for those specific cases using Django's low-level cache API. Ensure that manual invalidation is handled correctly when underlying data changes.","message":"Raw SQL queries (e.g., `QuerySet.extra`, `Model.objects.raw`, `cursor.execute`) are not cached by django-cachalot because it cannot reliably detect all affected tables for proper invalidation. Any data retrieved via raw SQL will bypass the cache.","severity":"gotcha","affected_versions":"All"},{"fix":"For multi-process deployments, use a shared cache backend like Redis (`django-redis`) or Memcached.","message":"Using `django.core.cache.backends.locmem.LocMemCache` (local memory cache) is not suitable for multi-process environments (e.g., Gunicorn with multiple workers, Celery, RQ) because the cache is not shared between processes, leading to stale data.","severity":"gotcha","affected_versions":"All"},{"fix":"After modifying `CACHALOT_CACHE`, run `./manage.py invalidate_cachalot` to clear the cache, or manually clear the old cache alias using its specific API if you need to retain other cache data.","message":"Changing the `CACHALOT_CACHE` setting to use a different cache alias in your `settings.py` will not automatically invalidate existing data in the *old* cache. This can lead to stale data being served until manually cleared.","severity":"gotcha","affected_versions":"All"},{"fix":"Upgrade to django-cachalot v2.9.0 or later to benefit from the fix for atomic transaction recursion issues.","message":"A recursion issue in atomic transactions (`transaction.atomic`) was fixed in v2.9.0. Prior versions might encounter unexpected behavior or errors when cachalot interacts with complex atomic blocks.","severity":"gotcha","affected_versions":"<2.9.0"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"In your `CACHES` setting for the `django-redis` backend, set `'OPTIONS': {'IGNORE_EXCEPTIONS': True}`. This configures `django-redis` to return default values (like an empty dictionary) on exceptions, allowing `cachalot` to gracefully fall back to database queries.","cause":"When `django-redis` is used as a cache backend and encounters a connection issue, it might return `None` for cache operations, which `cachalot`'s monkey-patching expects to be an iterable (e.g., an empty dictionary or list). This can crash the application.","error":"TypeError: 'NoneType' object is not iterable"},{"fix":"After performing external database modifications, manually invalidate the relevant cache entries using the `invalidate_cachalot` management command (`./manage.py invalidate_cachalot <app_label>[.<model_name>]`) or the `cachalot.api.invalidate()` function.","cause":"django-cachalot relies on Django ORM operations (INSERT, UPDATE, DELETE) to trigger cache invalidation. If you modify database data directly (e.g., via SQL console, external scripts, or database triggers) without using the Django ORM, cachalot will not be aware of these changes and will continue to serve stale cached data.","error":"Stale data displayed after modifying data outside Django's ORM."},{"fix":"Identify tables with high write frequencies. For these tables, consider excluding them from cachalot's caching using `CACHALOT_UNCACHABLE_TABLES` in your settings, or implement more granular caching strategies (e.g., per-object caching or manual caching for specific querysets) where appropriate.","cause":"django-cachalot's per-table invalidation strategy means that any write to a table invalidates *all* cached queries for that table. If a table is frequently updated, the cache might be invalidated more often than it's hit, leading to overhead without benefits or even a slowdown due to cache management.","error":"Performance doesn't improve or even degrades on pages with many write operations."},{"fix":"Set `CACHALOT_DATABASES` in your `settings.py` to explicitly list the database aliases where you want `django-cachalot` to be active. If your database is truly unsupported, you might also need to set `CACHALOT_USE_UNSUPPORTED_DATABASE = True` (though this is not recommended without careful testing).","cause":"By default, `django-cachalot` only caches queries on supported database engines (PostgreSQL, SQLite, MySQL). For custom or unsupported databases, it might be disabled by default.","error":"Queries using custom database aliases are not being cached."}]}