π 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 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.