๐Ÿ—ƒ Cached Viewsยถ

Duckโ€™s cached view system provides a high-performance, flexible way to cache the output of both synchronous and asynchronous views. It offers fine-grained control over what contributes to the cache key: request attributes, request callables, or fully custom Python callables.

Unlike traditional caching decorators, Duckโ€™s cached_view is designed for:

  • Deterministic cache keys

  • Dynamic argument/callable resolution

  • Async awareness

  • Safe component caching

  • Optional namespace isolation

  • Per-request debugging skips

  • Auto-adapted backends (sync/async)


โš™๏ธ Overviewยถ

The core concept:

@cached_view(targets=..., expiry=..., namespace=..., cache_backend=...)
def view(request, ...):
    ...

Where the targets determine what uniquely identifies the cached result.

Duck offers:

  • In-memory caching (InMemoryCache) as default

  • Pluggable backends with .get() / .set() API

  • Automatic wrapping for async โ†” sync compatibility

After the first call, subsequent invocations return the cached result instantly, skipping view execution entirely.


๐Ÿ›ธ cached_view Decoratorยถ

The decorator handles:

โœ” Request attribute-based cachingยถ

targets=["path", "method"]

โœ” Callable-based cachingยถ

targets={static_mtime: {'args': ()}}

โœ” Request callable targetsยถ

targets={'get_user_id': {'args': ()}}

โœ” External pure functionsยถ

targets={compute_hash: {'args': ('{request.path}',)}}

Targets are resolved before executing the view. The resolved values are then normalized into a stable, hashable structure (frozenset + tuple).


๐Ÿ“Œ Parametersยถ

Parameter

Type

Description

targets

List[str] or Dict[Union[str, Callable], Dict[str, Any]]

Required. Defines which request attributes or callable results contribute to the cache key.

expiry

Optional[float]

TTL in seconds; default is backend-defined.

cache_backend

any object with get(key) & set(key, value, ttl)

Custom cache backend (sync or async).

namespace

Optional[str or Callable]

Optional namespace prefix for cache isolation. Useful for per-user or per-tenant caching.

skip_cache_attr

str

Request attribute name that forces bypass of caching (for debugging). Default "skip_cache".

on_cache_result

Callable

Hook executed when a cached value is returned. Useful if the cached result requires reinitialization.

returns_static_response

bool

Indicates the response is static and safe to cache even if components are returned. Avoids safety warnings.


๐Ÿง  How Targets Workยถ

โœ” Attribute Targets (simple)ยถ

@cached_view(targets=["path", "method"])
def handler(request):
    return HttpResponse("OK")

โœ” Callable Targets (complex)ยถ

@cached_view(targets={static_mtime: {'args': None}})
def serve_static(request, staticfile):
    return FileResponse(...)

โœ” Request Callable Targetsยถ

@cached_view(targets={'compute_key': {'args': ('{request.method}',)}})
def handler(request):
    ...

โœ” External Functionsยถ

@cached_view(targets={my_hash_fn: {'args': ('{request.path}',)}})
def handler(request):
    ...

Dynamic formatting supported everywhereยถ

  • {request.path}

  • {request.user.id}

  • etc.


๐Ÿงฉ Namespace Supportยถ

Namespaces allow isolated cached values:

@cached_view(
    targets=['path'],
    namespace=lambda request: request.COOKIES.get('session_id')
)
def dashboard(request):
    ...

This means:

  • Each user gets their own cache bucket.

  • Cache invalidation is trivial: change namespace โ†’ old keys ignored.


๐Ÿ› Debugging with Skipยถ

request.skip_cache = True

or with a custom attribute:

@cached_view(targets=['path'], skip_cache_attr='debug_skip')

Allows bypassing cache for that single request.


๐Ÿช on_cache_result Hookยถ

A callable executed whenever a cached result is retrieved:

def rehydrate(component):
    component.rebind_runtime_state()

@cached_view(targets=['path'], on_cache_result=rehydrate)
def handler(request):
    ...

Useful for UI components, objects with state, etc.


๐Ÿงฑ Internal Behaviorยถ

1. Target Resolutionยถ

Extract values from:

  • request attributes

  • request callables

  • external Python callables

2. Key Constructionยถ

A stable structure:

(
    namespace_string_or_empty,
    frozenset(resolved_targets.items()),
    call_args,
    frozenset(call_kwargs.items())
)

This guarantees:

  • Deterministic hashing

  • Reproducibility

  • No collision from dict order

3. Backend Lookupยถ

Sync wrappers for async backends and vice-versa are created automatically.

4. View Execution or Cache Returnยถ

5. Cache Update (if allowed)ยถ

Stored with expiry if provided.


๐Ÿ›  Examplesยถ

Simpleยถ

@cached_view(targets=['path'])
def handler(request):
    return HttpResponse("OK")

Async Class-Based Viewยถ

class MyView(View):
    @cached_view(targets=['fullpath', 'method'])
    async def run(self):
        return HttpResponse("OK")

Callable-based Cachingยถ

@cached_view(targets={compute_hash: {'args': ('{request.path}',)}})
def handler(request):
    return HttpResponse("OK")

Static file cachingยถ

@cached_view(targets={static_mtime: {'args': None}})
def staticfiles(request, staticfile):
    return FileResponse(...)

โšก Best Practicesยถ

  1. Cache expensive or read-heavy views.

  2. Avoid caching highly personalized output unless namespaced.

  3. Use callable targets for:

    • mtime-based invalidation

    • database-derived cache keys

    • user-scoped session keys

  4. Use SkipViewCaching to disable caching when prerequisites fail.


โš– Comparison to Other Frameworksยถ

Django (cache_page, low-level API)ยถ

Feature

Django

Duck

Async support

partial

full & automatic

Callable targets

โŒ

โœ” full

Per-request skip

โŒ

โœ”

Namespaces

manual prefixing

built-in

Deterministic keys

basic

strong, structured

Auto backend sync/async

โŒ

โœ”

Dynamic format args

โŒ

โœ”

Flask (flask-caching)ยถ

Feature

Flask

Duck

Async

โŒ

โœ”

Callable targets

limited

โœ” arbitrary callables

Namespaced keys

manual

โœ”

Request attribute keys

partial

โœ” full

FastAPI (Depends + custom caching)ยถ

Feature

FastAPI

Duck

Decorator-based caching

โŒ (not built-in)

โœ”

Sync/async backend adaptation

manual

โœ”

Dynamic request formatting

โŒ

โœ”

Next.js (Edge/Static)ยถ

Feature

Next.js

Duck

Static response caching

strong

โœ” via returns_static_response

Dynamic callable keys

โŒ

โœ”

Per-user namespaces

tricky

โœ”

Overall:

Duckโ€™s cached_view is more granular, callable-aware, async-adaptive, and flexible than traditional caching decorators found in popular Python frameworks.


โœ… Summaryยถ

Duckโ€™s cached view system delivers:

  • Deterministic, stable cache keys

  • Full sync/async compatibility

  • Advanced callable & dynamic target resolution

  • Namespace-powered isolation

  • Instant debugging skip

  • Safe component caching

  • Backend abstraction

The result is a caching mechanism that is:

  • Faster

  • Safer

  • More expressive

  • More adaptive

  • More predictable

than typical caching decorators in Python frameworks.