Filters

A filter changes a value as it is written into a document. Put it after the value with |, and give it any values it needs in brackets:

{{ form.DraftFormName|upper }}
{{ form.als_fields_set.count() }} field{{ form.als_fields_set.count()|pluralize }}

Filters can follow one another, each working on what the one before it gave. To apply a filter to a whole block of a template rather than one value, use {% filter %}:

{% filter upper %}
This text is written in capitals.
{% endfilter %}

This page lists every filter a Document Template can use. Any other filter name stops the template with an error.

The examples use three values a real template would get from its data: when, a date and time of 5 March 2026 14:30 UTC; forms, three Forms with OIDs DM, AE and VS, of which AE is inactive; and form, the first of them. The Gives column is exactly what each example writes into the document.

Values are escaped

Text written into a document has &, <, > and quotes replaced by their escaped forms, so a value cannot break the document's markup. This is why Fish & Chips is written as Fish &amp; Chips. Filters that produce markup on purpose, such as markdown and clean_html, mark their result as safe so it is not escaped a second time.

This happens in every type of Template, Text included. A Text Template that writes plain text, Markdown or CSV rather than XML should write its values with safe, as in {{ form.DraftFormName|safe }}, so that they appear exactly as they are stored.

Two things are not escaped. The text the {% ai %} tag writes is used exactly as it comes back, and The AI tag lists the filters to use on it. The result of a {% filter %} block is not escaped again either, but the values inside it already were, so a filter that changes letters can break them: {% filter upper %}{{ "Fish & Chips" }}{% endfilter %} writes FISH &AMP; CHIPS. Apply such a filter to the value instead: {{ "Fish & Chips"|upper }}.

Filters added by TrialGrid

Filter

What it does

Example

Gives

chunk

Splits a list into lists of the length you give. The last one holds what is left.

{% for row in [1, 2, 3, 4, 5]|chunk(2) %}[{{ row|join(" ") }}] {% endfor %}

[1 2] [3 4] [5]

clean_html

Keeps only the HTML tags you list and removes the others, keeping their text. With no list, keeps a standard set of text formatting tags. On a Word, Excel or PDF Template, follow it with convert_html.

{{ "<p>Hello <span>there</span></p>"|clean_html(["p"]) }}

<p>Hello there</p>

convert_html

Turns HTML into Word or Excel formatting, so bold, lists and paragraphs appear as they would on a web page. Only in Word and Excel Templates.

date

Writes a date and time as year, month and day. Gives nothing for an empty value.

{{ when|date }}

2026-03-05

format_datetime

Writes a date and time in the format you give, using the codes in Python Date Formats. Gives nothing for an empty value.

{{ when|format_datetime("%d %b %Y %H:%M") }}

05 Mar 2026 14:30

html_sanitize

Removes HTML tags and attributes that are not on a fixed list of safe formatting, keeping their text.

{{ "<b>Bold</b><script>x()</script>"|html_sanitize }}

<b>Bold</b>x()

iso

Writes a date and time in the ISO 8601 format. Gives nothing for an empty value.

{{ when|iso }}

2026-03-05T14:30:00+00:00

line_breaks_br

Escapes the text, then turns each line break into <br/>. On a Word or Excel Template, follow it with convert_html.

{{ "one\ntwo"|line_breaks_br }}

one<br/>two

markdown

Turns Markdown into HTML. Any HTML already in the text is removed first. On a Word or Excel Template, follow it with convert_html.

{{ "Some **bold** text"|markdown }}

<p>Some <strong>bold</strong> text</p>

normalize_decimal

Writes a decimal number without trailing zeros or scientific notation. Any other value is written as it is.

{{ 2.50|normalize_decimal }}

2.5

pluralize

Applied to a number, gives s unless the number is 1, for adding to a word. For other endings, give the singular and plural ending separated by a comma.

3 Form{{ 3|pluralize }}, 1 Form{{ 1|pluralize }}, 2 Entr{{ 2|pluralize("y,ies") }}

3 Forms, 1 Form, 2 Entries

remove_line_break

Joins the lines of a text into one line, with a space between each. HTML in the text is escaped, so it is shown rather than used.

{{ "one\ntwo"|remove_line_break }}

one two

rml_sanitize

Turns HTML into the markup a PDF Template's paragraphs accept. Links are removed and their text kept, unless you pass True.

{{ "<p>See <a href='https://example.com'>this</a></p>"|rml_sanitize }}

See this

safe_json

Marks JSON text as safe to place inside a <script> block, replacing <, > and & with their JSON escapes.

{{ '{"a": "<b>"}'|safe_json }}

{"a": "\u003cb\u003e"}

slugify

Turns text into lowercase letters, numbers and hyphens, suitable for a file name or an anchor.

{{ "Adverse Events (AE)"|slugify }}

adverse-events-ae

strip_tags

Removes HTML tags, leaving their text.

{{ "<p>Hello <b>there</b></p>"|strip_tags }}

Hello there

timezone

Converts a date and time to the time zone you name. See List of Time Zones.

{{ when|timezone("America/New_York")|format_datetime("%H:%M") }}

09:30

utc

Writes a date and time with the name of its time zone. It does not convert the time: use timezone first for that.

{{ when|utc }}

2026-03-05 14:30:00 UTC

xml_escape

Escapes &, <, > and quotes, so text cannot break the markup of a PDF, Word or Excel Template.

{{ "Fish & Chips <hot>"|xml_escape }}

Fish &amp; Chips &lt;hot&gt;

convert_html has no example because what it gives is Word or Excel markup. See Generating Word Documents and Generating Excel Documents.

Filters from Jinja

These come with Jinja, the template language Document Templates are written in. The Jinja documentation describes each one in more depth.

Filter

What it does

Example

Gives

abs

The number without its sign.

{{ -3|abs }}

3

attr

An attribute of an object, by name.

{{ form|attr("OID") }}

DM

batch

Splits a list into lists of the length you give. A second value fills out the last one.

{% for row in [1, 2, 3]|batch(2, 0) %}[{{ row|join(" ") }}] {% endfor %}

[1 2] [3 0]

capitalize

The first letter upper case and the rest lower case.

{{ "adverse EVENTS"|capitalize }}

Adverse events

center

The text centred in the width you give.

[{{ "AE"|center(6) }}]

[  AE  ]

count

Another name for length.

{{ [1, 2, 3]|count }}

3

d

Another name for default.

{{ missing|d("none") }}

none

default

The value you give if the value is undefined. Pass true as a second value to use it for empty values as well.

{{ ""|default("none", true) }}

none

dictsort

The items of a dictionary sorted by key, or by value with by="value".

{% for k, v in {"b": 2, "a": 1}|dictsort %}{{ k }}={{ v }} {% endfor %}

a=1 b=2

e

Another name for escape.

{{ "<b>"|e }}

&lt;b&gt;

escape

Escapes &, <, > and quotes for HTML.

{{ "Fish & Chips"|escape }}

Fish &amp; Chips

filesizeformat

A number of bytes as a readable size.

{{ 1500000|filesizeformat }}

1.5 MB

first

The first item.

{{ ["DM", "AE"]|first }}

DM

float

The value as a decimal number.

{{ "2.5"|float + 1 }}

3.5

forceescape

Escapes for HTML even a value already marked safe.

{{ "<b>"|forceescape }}

&lt;b&gt;

format

Fills a printf-style pattern with the values you give.

{{ "%s has %d fields"|format("DM", 3) }}

DM has 3 fields

groupby

Groups a list of objects by an attribute. Each group has a grouper and its list.

{% for g in forms|groupby("DraftFormActive") %}{{ g.grouper }}: {{ g.list|map(attribute="OID")|join(", ") }}; {% endfor %}

False: AE; True: DM, VS;

indent

Indents every line but the first by the number of spaces you give. The example shows its line break as /.

{{ "a\nb"|indent(2)|replace("\n", "/") }}

a/  b

int

The value as a whole number.

{{ "42"|int + 1 }}

43

items

The key and value pairs of a dictionary, for a loop.

{% for k, v in {"a": 1}|items %}{{ k }}={{ v }}{% endfor %}

a=1

join

The items of a list joined with the text you give. attribute= joins one attribute of each.

{{ ["DM", "AE"]|join(", ") }}

DM, AE

last

The last item.

{{ ["DM", "AE"]|last }}

AE

length

How many items, or how many characters in a text. On a set of objects, use .count() instead, which does not read them.

{{ ["DM", "AE"]|length }}

2

list

The value as a list.

{{ "abc"|list|join("-") }}

a-b-c

lower

The text in lower case.

{{ "DM"|lower }}

dm

map

One attribute of each object (attribute=), or a filter applied to each item.

{{ forms|map(attribute="OID")|join(", ") }}

DM, AE, VS

max

The largest item. attribute= compares one attribute of each.

{{ [3, 7, 5]|max }}

7

min

The smallest item. attribute= compares one attribute of each.

{{ [3, 7, 5]|min }}

3

pprint

The value laid out for reading, for checking what a value holds.

{{ [1, 2]|pprint }}

[1, 2]

random

An item chosen at random.

reject

The items that fail a test.

{{ [1, 2, 3, 4]|reject("odd")|join(", ") }}

2, 4

rejectattr

The objects whose attribute fails a test, or is false if you name no test.

{{ forms|rejectattr("DraftFormActive")|map(attribute="OID")|join(", ") }}

AE

replace

The text with every occurrence of one piece replaced by another.

{{ "AE_FORM"|replace("_", " ") }}

AE FORM

reverse

The items, or the characters of a text, in the opposite order.

{{ ["DM", "AE"]|reverse|join(", ") }}

AE, DM

round

The number rounded to the places you give. method='floor' or method='ceil' rounds down or up.

{{ 2.567|round(1) }}

2.6

safe

Marks a value as safe, so it is not escaped. Use it on a value you know cannot break the document's markup, or in a Text Template whose output is not markup.

{{ "<b>bold</b>"|safe }}

<b>bold</b>

select

The items that pass a test.

{{ [1, 2, 3, 4]|select("odd")|join(", ") }}

1, 3

selectattr

The objects whose attribute passes a test, or is true if you name no test.

{{ forms|selectattr("DraftFormActive")|map(attribute="OID")|join(", ") }}

DM, VS

slice

Splits a list into the number of columns you give.

{% for column in [1, 2, 3, 4, 5]|slice(2) %}[{{ column|join(" ") }}] {% endfor %}

[1 2 3] [4 5]

sort

The items in order. reverse=true for largest first, attribute= to sort objects by one of their attributes.

{{ [3, 1, 2]|sort(reverse=true)|join(", ") }}

3, 2, 1

string

The value as text.

{{ (1)|string + "st" }}

1st

striptags

Removes HTML tags and runs of spaces.

{{ "<p>Hello  <b>there</b></p>"|striptags }}

Hello there

sum

The items added up. attribute= adds one attribute of each.

{{ [1, 2, 3]|sum }}

6

title

Each word starting with a capital letter.

{{ "adverse events"|title }}

Adverse Events

tojson

The value as JSON, safe to place inside a <script> block.

{{ {"a": [1, 2]}|tojson }}

{"a": [1, 2]}

trim

The text without spaces at the start and end.

[{{ "  AE  "|trim }}]

[AE]

truncate

The text cut to the length you give, ending in ....

{{ "Adverse Events Log"|truncate(12, killwords=true) }}

Adverse E...

unique

The items without duplicates.

{{ ["CheckBox", "Text", "CheckBox"]|unique|join(", ") }}

CheckBox, Text

upper

The text in upper case.

{{ "dm"|upper }}

DM

urlencode

The text encoded for use in a web address.

{{ "a b&c"|urlencode }}

a%20b%26c

urlize

Turns web addresses in a text into HTML links. On a Word or Excel Template, follow it with convert_html.

{{ "See https://example.com"|urlize }}

See <a href="https://example.com" rel="noopener">https://example.com</a>

wordcount

How many words in a text.

{{ "Adverse Events Log"|wordcount }}

3

wordwrap

Wraps a text at the width you give, with a line break or the wrapstring you give.

{{ "Adverse Events Log"|wordwrap(10, wrapstring=" / ") }}

Adverse / Events Log

xmlattr

Turns a dictionary into HTML or XML attributes.

<td{{ {"class": "oid"}|xmlattr }}>

<td class="oid">

random has no example because what it gives changes every time.