Skip to content

Latest commit

 

History

History
259 lines (187 loc) · 5.1 KB

File metadata and controls

259 lines (187 loc) · 5.1 KB

Custom Filters

Custom filters allow you to extend Jinja2's built-in filters with your own Python functions.

Quick Start

# Import a single filter function
$ jinja2 template.j2 data.json -F mymodule.myfilter

# Import all filters from a module
$ jinja2 template.j2 data.json -F mymodule

Writing Custom Filters

Create a Python module with filter functions:

# myfilters.py
def reverse(s):
    """Reverse a string"""
    return s[::-1]

def uppercase(s):
    """Convert to uppercase"""
    return s.upper()

def multiply(value, factor=2):
    """Multiply a value by a factor"""
    return value * factor

Use in your template:

{{ "hello" | reverse }}
{{ "hello" | uppercase }}
{{ 5 | multiply(3) }}

Run:

$ jinja2 template.j2 data.json -F myfilters

Import Patterns

Auto-discovery from module

Import all public functions from a module:

$ jinja2 template.j2 data.json -F myfilters

This will import all functions that don't start with _.

Specific function

Import just one filter function:

$ jinja2 template.j2 data.json -F myfilters.reverse

Multiple filters

Import from multiple modules or functions:

$ jinja2 template.j2 data.json -F myfilters.reverse -F otherfilters.double

Filters dict

If your module has a filters dict, it will be used:

# myfilters.py
def reverse(s):
    return s[::-1]

def shout(s):
    return s.upper() + "!"

filters = {
    'reverse': reverse,
    'shout': shout,
}
$ jinja2 template.j2 data.json -F myfilters
# or explicitly reference the dict
$ jinja2 template.j2 data.json -F myfilters.filters

Loader functions

Functions starting with load_ or named filters, get_filters, or load_filters are treated as loader functions that return a dict of filters:

# myfilters.py
def load_custom_filters():
    return {
        'reverse': lambda s: s[::-1],
        'double': lambda n: n * 2,
    }
$ jinja2 template.j2 data.json -F myfilters.load_custom_filters

Using Ansible Filters

Ansible filters are supported with special handling for the FilterModule pattern:

Simplified syntax

# Load all Ansible core filters
$ jinja2 template.j2 data.json -F ansible.plugins.filter.core

# Load ipaddr filters
$ jinja2 template.j2 data.json -F ansible.plugins.filter.ipaddr

# Load netaddr filters
$ jinja2 template.j2 data.json -F ansible.plugins.filter.netaddr

Explicit FilterModule class

$ jinja2 template.j2 data.json -F ansible.plugins.filter.core.FilterModule

Example template using Ansible filters

{# Using the combine filter #}
{% set defaults = {'port': 8080, 'host': 'localhost'} %}
{% set custom = {'port': 3000} %}
{{ defaults | combine(custom) | to_json }}

{# Using ipaddr filter #}
{{ '192.168.1.1' | ipaddr }}
$ jinja2 template.j2 data.json -F ansible.plugins.filter.core -F ansible.plugins.filter.ipaddr

Local vs Installed Modules

Installed modules

If a module is importable (installed via pip or on PYTHONPATH), it will be loaded from the environment:

$ pip install my-custom-filters
$ jinja2 template.j2 data.json -F my_custom_filters

Local modules

If the module is not importable, jinja2-cli will look for it in the current directory:

project/
├── filters/
│   └── custom.py
├── template.j2
└── data.json
$ cd project
$ jinja2 template.j2 data.json -F filters.custom

Advanced: Filter Classes

Classes with a filters() method are automatically instantiated and their filters are loaded:

# myfilters.py
class FilterModule:
    """Ansible-style filter module"""
    
    def filters(self):
        return {
            'my_filter': self.my_filter,
            'my_other_filter': self.my_other_filter,
        }
    
    def my_filter(self, value):
        return value.upper()
    
    def my_other_filter(self, value):
        return value[::-1]
$ jinja2 template.j2 data.json -F myfilters.FilterModule
# or let auto-discovery find it
$ jinja2 template.j2 data.json -F myfilters

Real-world Examples

Network configuration with Ansible filters

{# template.j2 #}
{% for interface in interfaces %}
interface {{ interface.name }}
  ip address {{ interface.ip | ipaddr('address') }}
  netmask {{ interface.ip | ipaddr('netmask') }}
{% endfor %}
$ jinja2 template.j2 network.json -F ansible.plugins.filter.ipaddr

Data transformation

# transforms.py
import json
from datetime import datetime

def to_timestamp(date_string, format='%Y-%m-%d'):
    """Convert date string to Unix timestamp"""
    dt = datetime.strptime(date_string, format)
    return int(dt.timestamp())

def pretty_json(obj):
    """Pretty-print JSON"""
    return json.dumps(obj, indent=2)

def flatten(nested_list):
    """Flatten a nested list"""
    return [item for sublist in nested_list for item in sublist]
{# template.j2 #}
{{ date | to_timestamp }}
{{ data | pretty_json }}
{{ nested | flatten | join(', ') }}
$ jinja2 template.j2 data.json -F transforms