Adds caching support to Flask applications.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Jinja2 template integration for caching expensive template fragments. This feature allows caching portions of templates to improve rendering performance, especially useful for complex layouts, database-driven content, or expensive template operations.
Flask-Caching automatically registers a Jinja2 extension that provides template-level caching control through the {% cache %} template tag.
class CacheExtension(Extension):
"""
Jinja2 extension for template fragment caching.
Automatically registered when Cache is initialized with with_jinja2_ext=True.
"""
# Template syntax
"""
{% cache timeout key1[, key2, ...] %}
...template content...
{% endcache %}
"""Automatic Registration:
from flask import Flask
from flask_caching import Cache
app = Flask(__name__)
cache = Cache(app) # Jinja2 extension automatically registered
# Disable automatic registration
cache = Cache(app, with_jinja2_ext=False)Cache template fragments with timeout and optional cache keys.
{# Cache for 5 minutes with default key #}
{% cache 300 %}
<div class="expensive-content">
{% for item in expensive_database_query() %}
<p>{{ item.title }}: {{ item.description }}</p>
{% endfor %}
</div>
{% endcache %}
{# Cache with custom key #}
{% cache 600 "sidebar_content" %}
<aside class="sidebar">
{{ render_complex_sidebar() }}
</aside>
{% endcache %}
{# Cache with multiple keys for uniqueness #}
{% cache 300 "user_dashboard" user.id %}
<div class="dashboard">
<h2>Welcome {{ user.name }}</h2>
{{ render_user_stats(user.id) }}
</div>
{% endcache %}Use template variables and expressions to create dynamic cache keys based on context.
{# Cache per user and page #}
{% cache 600 "user_content" user.id request.endpoint %}
<div class="personalized-content">
{{ get_user_recommendations(user.id) }}
</div>
{% endcache %}
{# Cache with date-based key for daily content #}
{% cache 3600 "daily_stats" current_date.strftime('%Y-%m-%d') %}
<div class="daily-statistics">
{{ calculate_daily_metrics() }}
</div>
{% endcache %}
{# Cache with conditional keys #}
{% cache 300 "product_list" category.id, ("sale" if on_sale else "regular") %}
<div class="product-grid">
{% for product in get_products(category.id, on_sale) %}
{{ render_product_card(product) }}
{% endfor %}
</div>
{% endcache %}Special cache operations within templates.
{# Delete cached content #}
{% cache 'del' "sidebar_content" %}
{# This will delete the cached content and re-render #}
<aside class="sidebar">
{{ render_complex_sidebar() }}
</aside>
{% endcache %}
{# Cache with no timeout (never expires) #}
{% cache None "permanent_footer" %}
<footer>
{{ render_footer_content() }}
</footer>
{% endcache %}Generate cache keys programmatically for template fragments.
def make_template_fragment_key(
fragment_name: str,
vary_on: Optional[List[str]] = None
) -> str:
"""
Generate cache key for template fragments.
Parameters:
- fragment_name: Base name for the cache fragment
- vary_on: List of values to vary the cache key on
Returns:
String cache key for the template fragment
"""Usage in Python Code:
from flask_caching import make_template_fragment_key
# Generate keys manually
cache_key = make_template_fragment_key('user_profile', [str(user.id)])
cached_content = cache.get(cache_key)
if cached_content is None:
# Render template fragment
cached_content = render_template_string("""
<div class="profile">{{ user.name }}</div>
""", user=user)
cache.set(cache_key, cached_content, timeout=300)
# Delete specific fragment cache
cache_key = make_template_fragment_key('sidebar_content')
cache.delete(cache_key)
# Pre-warm cache
def pre_warm_user_cache(user_id):
cache_key = make_template_fragment_key('user_dashboard', [str(user_id)])
if not cache.has(cache_key):
user = User.query.get(user_id)
content = render_template('fragments/user_dashboard.html', user=user)
cache.set(cache_key, content, timeout=600)Manage cached template fragments from Python code.
from flask import current_app
from flask_caching import make_template_fragment_key
def clear_user_template_cache(user_id):
"""Clear all cached template fragments for a user."""
fragment_keys = [
make_template_fragment_key('user_dashboard', [str(user_id)]),
make_template_fragment_key('user_profile', [str(user_id)]),
make_template_fragment_key('user_sidebar', [str(user_id)])
]
cache.delete_many(*fragment_keys)
def invalidate_category_cache(category_id):
"""Invalidate product listing caches for a category."""
keys_to_delete = [
make_template_fragment_key('product_list', [str(category_id), 'regular']),
make_template_fragment_key('product_list', [str(category_id), 'sale']),
make_template_fragment_key('category_sidebar', [str(category_id)])
]
cache.delete_many(*keys_to_delete)
def refresh_global_fragments():
"""Refresh site-wide cached fragments."""
global_fragments = ['header_nav', 'footer_content', 'sidebar_ads']
for fragment in global_fragments:
cache_key = make_template_fragment_key(fragment)
cache.delete(cache_key)Complex caching scenarios and best practices.
Nested Fragment Caching:
{# Outer cache for entire section #}
{% cache 1800 "news_section" %}
<section class="news">
<h2>Latest News</h2>
{# Inner cache for expensive news list #}
{% cache 300 "news_list" %}
<div class="news-list">
{% for article in get_latest_news() %}
<article>{{ article.title }}</article>
{% endfor %}
</div>
{% endcache %}
{# Separate cache for sidebar #}
{% cache 600 "news_sidebar" %}
<aside>{{ render_news_sidebar() }}</aside>
{% endcache %}
</section>
{% endcache %}Conditional Caching:
{# Cache only for non-admin users #}
{% if not current_user.is_admin %}
{% cache 300 "public_content" %}
{% endif %}
<div class="content">
{{ expensive_content_generation() }}
</div>
{% if not current_user.is_admin %}
{% endcache %}
{% endif %}User-Specific Fragment Caching:
{# Cache personalized content per user #}
{% cache 600 "personalized_recommendations" current_user.id %}
<div class="recommendations">
<h3>Recommended for {{ current_user.name }}</h3>
{% for item in get_user_recommendations(current_user.id) %}
<div class="recommendation">{{ item.title }}</div>
{% endfor %}
</div>
{% endcache %}
{# Cache with user role differentiation #}
{% cache 900 "dashboard_widgets" current_user.id current_user.role %}
<div class="dashboard-widgets">
{% if current_user.role == 'admin' %}
{{ render_admin_widgets() }}
{% elif current_user.role == 'manager' %}
{{ render_manager_widgets() }}
{% else %}
{{ render_user_widgets() }}
{% endif %}
</div>
{% endcache %}The template cache extension integrates seamlessly with Flask-Caching's main cache instance.
Cache Extension Constants:
JINJA_CACHE_ATTR_NAME = "_template_fragment_cache"
# Attribute name used to attach cache instance to Jinja2 environmentManual Extension Usage:
from flask_caching.jinja2ext import CacheExtension, JINJA_CACHE_ATTR_NAME
# Manual registration (when with_jinja2_ext=False)
app.jinja_env.add_extension(CacheExtension)
setattr(app.jinja_env, JINJA_CACHE_ATTR_NAME, cache)
# Access cache from template context
def get_template_cache():
return getattr(current_app.jinja_env, JINJA_CACHE_ATTR_NAME)Cache Key Strategy:
Cache Timeout Strategy:
Memory Usage:
Install with Tessl CLI
npx tessl i tessl/pypi-flask-caching