django-cacheops

7.2 · active · verified Tue Apr 14

django-cacheops is a slick ORM cache for Django, offering automatic granular event-driven invalidation. It leverages Redis for high-performance caching. Currently at version 7.2, it maintains an active development cycle with regular updates supporting recent Django and Python versions.

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to configure django-cacheops with minimal Django settings and use its core features: caching querysets with `.cache()`, explicit model invalidation, and the `@cached_queryset` decorator for functions returning querysets. It assumes a Redis server is accessible at `redis://localhost:6379/1` or specified via the `CACHEOPS_REDIS` environment variable.

import os
import django
from django.conf import settings
from django.db import models

# Minimal Django settings for a runnable example
# In a real project, these would be in your settings.py
if not settings.configured:
    settings.configure(
        INSTALLED_APPS=[
            'django.contrib.auth',
            'django.contrib.contenttypes',
            'cacheops', # Add cacheops to your installed apps
            'myapp',    # A dummy app for model definition
        ],
        CACHEOPS_REDIS=os.environ.get('CACHEOPS_REDIS', 'redis://localhost:6379/1'),
        # CACHEOPS_ENABLED defaults to True since v7.0
        # CACHEOPS_TIMEOUT defaults to 3600 (1 hour) since v7.0
        DATABASES={
            'default': {
                'ENGINE': 'django.db.backends.sqlite3',
                'NAME': ':memory:',
            }
        },
        USE_TZ=True,
        TIME_ZONE='UTC',
    )
django.setup()

# Define a simple Django model. For demonstration, we use 'myapp' as app_label.
class Post(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        app_label = 'myapp' # Required when defining models outside an actual app directory

    def __str__(self):
        return self.title

# Ensure the database schema is created for the model (for in-memory SQLite)
from django.db import connection
with connection.schema_editor() as schema_editor:
    schema_editor.create_model(Post)

print(f"Using Redis connection string: {settings.CACHEOPS_REDIS}")

# Create some sample data
Post.objects.create(title="First Post", body="Content of the first post.")
Post.objects.create(title="Second Article", body="More content here.")

# --- Example 1: Caching a Queryset ---
print("\n--- Example 1: Caching a Queryset ---")
# The .cache() method makes the queryset result be cached.
# First call will hit the database and store the result in cache.
posts = Post.objects.filter(title__icontains='post').cache().all()
print(f"Posts (first fetch, hits DB and caches): {[p.title for p in posts]}")

# Subsequent calls with the same query parameters will hit the cache.
posts_cached = Post.objects.filter(title__icontains='post').cache().all()
print(f"Posts (second fetch, should hit cache): {[p.title for p in posts_cached]}")

# --- Example 2: Invalidating Cache ---
from cacheops import invalidate_model

print("\n--- Example 2: Invalidating Cache ---")
# Update an object (usually auto-invalidates if configured)
post_to_update = Post.objects.get(title="First Post")
post_to_update.title = "Updated First Post"
post_to_update.save()

# Explicitly invalidate all caches related to the Post model
invalidate_model(Post)
print("Post model cache explicitly invalidated.")

# Fetching again will hit the DB to get the updated data and re-cache.
updated_posts = Post.objects.filter(title__icontains='post').cache().all()
print(f"Posts after update and invalidation: {[p.title for p in updated_posts]}")

# --- Example 3: Using @cached_queryset decorator ---
from cacheops import cached_queryset

print("\n--- Example 3: Using @cached_queryset decorator ---")
@cached_queryset
def get_latest_articles(limit=1):
    print("  (Fetching from DB inside get_latest_articles)")
    return Post.objects.order_by('-created_at')[:limit]

# First call of the decorated function
latest1 = get_latest_articles()
print(f"Latest article (first call): {[p.title for p in latest1]}")

# Second call of the decorated function should hit the cache
latest2 = get_latest_articles()
print(f"Latest article (second call, should hit cache): {[p.title for p in latest2]}")

view raw JSON →