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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions