πŸ—ƒ Cached Views – High-Performance Python Web AppsΒΆ

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.