Skip to content

Django User Guide

This guide covers how to use ZooCache with Django effectively.

Table of Contents

  1. Installation
  2. Quick Start
  3. Core Concepts
  4. Advanced Features
  5. Configuration
  6. Best Practices
  7. Troubleshooting

Installation

To use the Django ORM adapter, install ZooCache with the django extra:

Using uv (recommended):

uv add "zoocache[django]"

Using pip:

pip install zoocache[django]


Quick Start

1. Add ZooCacheManager to your Models

To enable caching for a model, add ZooCacheManager as a manager. It's recommended to keep the default objects manager for non-cached queries.

from django.db import models
from zoocache.contrib.django import ZooCacheManager

class Product(models.Model):
    name = models.CharField(max_length=100)
    category = models.CharField(max_length=50)

    objects = models.Manager()          # Standard manager
    cached = ZooCacheManager(ttl=300)   # ZooCache manager

2. Querying

Use the cached manager exactly like you would use standard Django managers:

# Hits the database and populates the cache
products = Product.cached.filter(category="electronics")

# Subsequent calls with the same filters hit the cache
products = Product.cached.filter(category="electronics")

Core Concepts

Automatic Invalidation

ZooCache automatically invalidates cached results for a model whenever an instance is saved or deleted.

# Cached query
Product.cached.all()

# Saving any instance invalidates ALL cached queries for Product
p = Product.objects.get(id=1)
p.name = "New Name"
p.save() 

Transaction Support

ZooCache is transaction-aware. Invalidation is deferred until the current database transaction is successfully committed.

from django.db import transaction

with transaction.atomic():
    product.save()
    # Cache is still valid here (pending commit)

# Cache is invalidated ONLY after this point

Advanced Features

Dependency Detection (JOINs)

When you perform a query that involves JOINs, ZooCache automatically detects the related models and registers them as dependencies.

# This query depends on BOTH Book and Author models
books = Book.cached.filter(author__name="Isaac Asimov")

# Saving an Author will invalidate this Book query!
author.save()

Query Optimizations

ZooCache works seamlessly with select_related and prefetch_related.

# Related objects are cached alongside the main entity
books = Book.cached.select_related("author").all()

for book in books:
    print(book.author.name)  # No database hit!

Configuration

Manager Parameters

Parameter Type Description
ttl int Override the default TTL for this manager's queries.
prefix str Optional namespace prefix for cache keys.
ensure_objects_manager bool If True (default), automatically injects objects = models.Manager() if not defined.

[!TIP] By default, ZooCache ensures that an objects manager is available. This prevents the common Django issue where adding a custom manager removes the default objects manager.

# 'objects' is automatically available even if not defined
cached = ZooCacheManager(ttl=60, prefix="fast_cache")

Global Configuration

The preferred way to configure ZooCache in Django is by adding a ZOOCACHE dictionary to your settings.py. ZooCache will automatically detect these settings.

# settings.py

ZOOCACHE = {
    "storage_url": "redis://localhost:6379",
    "bus_url": "redis://localhost:6379",
    "default_ttl": 3600,
}

Alternative: Manual Configuration

If you need more control or want to configure ZooCache programmatically, you can still use the AppConfig.ready() pattern:

# myapp/apps.py
from django.apps import AppConfig
import zoocache

class MyAppConfig(AppConfig):
    name = 'myapp'

    def ready(self):
        # This will only run if ZOOCACHE is not defined in settings.py
        zoocache.configure(
            storage_url="redis://localhost:6379",
            default_ttl=3600
        )

Best Practices

  1. Keep objects as Default: Always keep a standard models.Manager() to avoid accidental caching in administrative or write-heavy tasks.
  2. Monitor Model Write Frequency: Models that are updated frequently (e.g., Session, Log) will have a low cache hit rate because every save invalidates the whole model's cache.
  3. Use for Read-Heavy Views: Identify your most expensive read queries and use .cached there for the biggest performance gains.

Troubleshooting

Stale Data after Bulk Operations

Django's .update(), .bulk_create(), and .bulk_update() do not trigger signals. Use manual invalidation if needed:

from zoocache import invalidate

# Invalidate all Product queries
invalidate("django.model:myapp.product")

Large Cache Size

If your cache grows too large, ensure you have configured auto_prune_secs in your global ZooCache configuration.