Skip to content

render_j2_template fallback environment missing custom filters (to_json) when custom templates.path is set #1236

Description

@aawemove

When server.templates.path is configured in pycsw.yml, render_j2_template() in pycsw/ogc/api/util.py creates a custom Jinja2 Environment and correctly registers the to_json filter on it. However, when a requested template is not found in the custom path, the fallback Environment (pointing to the built-in templates) is created without registering to_json. Any built-in template that uses {{ ... | to_json }} then fails with a TemplateAssertionError at render time.

To reproduce

  1. Set server.templates.path in pycsw.yml to a custom directory
  2. Place only the templates you want to override in that directory (e.g. item.html) — intentionally omit others (e.g. items.html)
  3. Request an endpoint whose template is not in the custom directory (e.g. GET /collections/{collectionId}/items?f=html)

Expected behavior

The fallback should transparently use the built-in template, including all registered filters — identical to the behavior when no custom templates.path is set. Only the templates present in the custom directory should be overridden; all others should fall back to the built-in defaults without error.

Actual behavior

HTTP 500. The fallback Environment renders the built-in items.html, which calls {{ data['features'] | to_json }}, but to_json is not registered on the fallback environment:

jinja2.exceptions.TemplateAssertionError: No filter named 'to_json'.

Root cause

In pycsw/ogc/api/util.py, render_j2_template():

env = Environment(loader=FileSystemLoader(templates_path))
custom_templates = True

env.filters['to_json'] = to_json          # ✅ registered on custom env
env.globals.update(to_json=to_json)

try:
    template = env.get_template(template)
except TemplateNotFound as err:
    if custom_templates:
        env = Environment(loader=FileSystemLoader(TEMPLATES))  # ❌ new env, no to_json
        template = env.get_template(template)

return template.render(...)               # crashes on {{ ... | to_json }}

Suggested fix

Register the filter on the fallback environment as well:

except TemplateNotFound as err:
    if custom_templates:
        env = Environment(loader=FileSystemLoader(TEMPLATES))
        env.filters['to_json'] = to_json   # ← add this
        env.globals.update(to_json=to_json)
        template = env.get_template(template)

Or extract filter registration into a helper to avoid duplication:
  
def _make_env(path):
    env = Environment(loader=FileSystemLoader(path))
    env.filters['to_json'] = to_json
    env.globals.update(to_json=to_json)
    return env

Impact

Without this fix, templates.path effectively requires users to copy all built-in templates into their custom directory — including ones they do not want to modify — to avoid hitting the broken fallback. This defeats the purpose of being able to override individual templates.

Environment

  • pycsw version: 3.0.dev0
  • Python: 3.12
  • Deployment: Docker (geopython/pycsw:latest)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions