Template Helpers¶
Template helpers are Python functions that are exposed to CKAN's Jinja2
templating system. They allow templates to call custom logic to format data,
fetch config properties, or retrieve dynamic dataset information using the h
global object (e.g., h.myextension_some_helper()).
Naming and Namespacing¶
In older CKAN extensions, helpers were often defined with generic names (e.g.,
get_featured_datasets or get_all_groups). This creates conflicts if CKAN
core or other extensions register helpers with the same name.
Prefix Your Helpers
Always prefix your helper functions with your extension's name (e.g.,
myextension_recently_updated_info instead of recently_updated_info). This
guarantees namespace safety.
Best Practices for Modern Helpers¶
Centralize Configuration Access¶
Instead of reading raw configurations inside helpers using
tk.config.get(...), wrap config parameters in a dedicated config.py module
inside your extension and call the config helper function.
Use Action APIs Instead of Direct DB Queries¶
Older extensions often query database tables (e.g., model.Session.query(model.PackageExtra)) directly inside helpers as a fallback when search indexes (Solr) are slow. This bypasses search caches and couples your extension to the internal database schema, which can change between CKAN versions.
Query via search API
Always use action API calls (such as package_search or package_show) to query datasets instead of writing database-level SQLAlchemy queries.
import ckan.model as model
def get_featured_datasets():
# DB fallback is slow and Couples model to DB schema
extras_query = model.Session.query(model.PackageExtra).filter(
model.PackageExtra.key == 'is_featured', # (1)!
model.PackageExtra.value == 'True'
).all()
# ... logic to fetch packages ...
- This code may work in CKAN v2.11, but it breaks in v2.12, because
PackageExtramodel was removed.
Write Typed and Annotated Helpers¶
Always supply PEP 484 type annotations for parameters and return types. Even though helpers are usually called from templates, where type-checker has no power, typing information can be used to detect internal anomalies inside helper implementation.
def my_count_uploaded_resources(pkg: dict[str, Any]) -> int:
"""Counts the number of uploaded resources in a package."""
count = 0
for res in pkg.get("resources", []):
if res.get("url_type") == "upload":
count += 1
return count
Auto-Registering Helpers¶
CKAN extensions register all helper functions automatically by applying the
@tk.blanket.helpers decorator to the plugin class in plugin.py.
import ckan.plugins as p
import ckan.plugins.toolkit as tk
@tk.blanket.helpers # (1)
class MyPlugin(p.SingletonPlugin):
pass
- Discovery scans your
helpers.pyfile and exposes every defined function to the Jinja2 template context under thehnamespace (e.g.,h.my_count_uploaded_resources(package)).