๐ 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 defaultPluggable backends with
.get()/.set()APIAutomatic 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 |
|---|---|---|
|
|
Required. Defines which request attributes or callable results contribute to the cache key. |
|
|
TTL in seconds; default is backend-defined. |
|
any object with |
Custom cache backend (sync or async). |
|
|
Optional namespace prefix for cache isolation. Useful for per-user or per-tenant caching. |
|
|
Request attribute name that forces bypass of caching (for debugging). Default |
|
|
Hook executed when a cached value is returned. Useful if the cached result requires reinitialization. |
|
|
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ยถ
Cache expensive or read-heavy views.
Avoid caching highly personalized output unless namespaced.
Use callable targets for:
mtime-based invalidation
database-derived cache keys
user-scoped session keys
Use
SkipViewCachingto 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 |
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.