Skip to main content

Jinja Template Language

Overview

Docwize uses Jinja for template building. These can be templates for Word documents, dynamic stamps, email templates, etc. Templates in Jinja are text files that combine variables and expressions (which display values) with tags (which control logic). Think of them as blueprints for generating any text-based format, like HTML or XML.

For an extensive overview of Jinja, please see: https://jinja.palletsprojects.com/en/stable/templates/#

For assistance while creating Jinja templates, we recommend using your preferred AI assistant.

Key Concepts

Variables

These are placeholders that get replaced with actual values when the template is used.

o A user can access parts of a variable using a dot (.) or square brackets ([]), like {{ user.name }} or {{ user['name'] }}.

o If a variable doesn't exist, it causes an error.

Tags

These control how a user's template works.

o Statements ({% ... %}): For actions like loops (for) or conditions (if).

o Expressions ({{ ... }}): For displaying values.

o Comments ({# ... #}): For notes that won't appear in the final output.

Filters

Modify variables. Use a pipe | to apply them.

o Example: {{ name|striptags|title }} removes HTML and then capitalizes each word.

Tests

Check if a variable meets certain conditions. Use is to apply them.

o Example: {% if name is defined %} checks if name exists.

Template Inheritance

This powerful feature lets users create a base template with common parts (like headers and footers) and then "child" templates that fill in specific sections.

o {% block content %}: Defines a section in a template that a child template can fill or change.

o {{ super() }}: In a child block, this calls the content from the same block in the parent template.

Expressions

Pieces of code that produce a value. They are used to display dynamic content within templates. Think of them as the "output" part of the template logic. While tags control the flow (if, for, set), expressions are what actually show data to the user.

Syntax

Expressions are always enclosed in double curly braces: {{ ... }}

How Expressions Work and What They Include

Displaying Variables

The most common use of an expression is to simply output the value of a variable that has been passed to the template. For example:

{{ document.description}}: If a document with a document description is available, this will print the document description's name.

{{ document[reference_number]}}: Accessing a value from a dictionary (or an attribute) using bracket notation.

Using Filters

As mentioned before, expressions are where users apply filters to modify the output of a variable.

{{ document.doc_type | upper }}: Converts the document type variable to all uppercase before printing.

{{ document.description | striptags }}: Removes HTML tags from the document description variable.

{{ document.date | default("N/A") }}: Displays the document's date, or "N/A" if unavailable.

Basic Math Operations

Users can perform simple arithmetic directly within expressions. While complex calculations are usually best done in Python code before sending data to the template, basic math is supported.

{{ 10 / 3 }}: Displays 3.333... (floating-point division).

{{ 10 // 3 }}: Displays 3 (integer division).

{{ 5 % 2 }}: Displays 1 (remainder).

Note: The specific document properties provided are not typically used for mathematical calculations in this manner. The examples above demonstrate general Jinja math operations.

String Concatenation (Joining Text)

Uers can combine strings using the tilde ~ operator. This is useful for building dynamic strings.

{{ "Document " ~ document.doc_number ~ " from " ~ document.from_person ~ " (" ~ document.from_entity ~ ")" }}:

This outputs a combined string like "Document INV-001 from John Doe (Acme Corp)".

Calling Methods/Functions (that return a value)

If a variable is an object that has methods, users can call those methods within an expression, and the result of the method call will be displayed.

{{ document.description.capitalize() }}: If document.description is "initial draft", this might output "Initial draft".

Literals

Users can directly include values like strings, numbers, lists, or dictionaries. These are often used as arguments for filters or functions.

{{ "This is a literal string" }}

{{ 12345 }}

{{ ['Invoice', 'Contract', 'Report'] | join(', ') }}: Uses a literal list of document types and applies a filter to join them with a comma and space.

If Expressions (Ternary Operator)

A concise way to display one value if a condition is true, and another if it's false.

{{ "Document approved" if document.doc.status == "approved" else "Document pending" }}: If document.doc.status is 'approved', it displays "Document approved"; otherwise, "Document pending".

Controlling Whitespace

Jinja normally keeps most whitespace, but users can control it.

Add a minus sign (-) next to a tag delimiter to remove whitespace:

{%- for item in seq %} removes leading whitespace.

{{ item }}{%- endfor %} removes trailing whitespace.

Common Control Structures

For Loops: Repeat content for each item in a list.

<ul>

{% for document in documents%}

<li>{{ document.description|e }}</li>

{% else %}

<li>No documents found.</li>

{% endfor %}

</ul>

Users can access special loop variables (like loop.index for the current count).

Use {% else %} for content when the list is empty.

If Statements: Show content based on a condition.

{% if document.doc_status == 'approved' %}

This document is approved and ready for publication.

{% elif document.doc_status == 'pending_review' %}

This document is currently awaiting review by the team.

{% elif document.type == 'draft' %}

This is a draft document. It needs further work before finalization.

{% else %}

This document's status is unknown or requires attention.

{% endif %}

Macros: Reusable pieces of code, like functions.

{% macro display_doc_info_simple(doc_number, doc_type, doc_status) %}

Document: {{ doc_number | default("N/A") }{ | Type: {{ doc_type | default("N/A") }{ | Status: {{ doc_status | default("N/A") }{

{% endmacro %}

If a macro is in another file, users need to import it first.

Jinja Code Examples and Their Outcomes

Here are Jinja code examples within Docwize and an explanation of what they do and their likely output:

1. Displaying Variables and Applying Filters

These examples show how to output specific document attributes and apply filters for formatting or sanitization.

Example A: Displaying Document Number with Default

{{ document.doc_number | default("N/A") }}

Explanation: This expression outputs the value of the doc_number attribute from a document variable. The default("N/A") filter ensures that if document.doc_number is None, an empty string, or not defined, the string "N/A" will be displayed instead, preventing blank outputs for missing data.

Outcome:

If document is {'doc_number': 'INV-2024-001'}: INV-2024-001

If document is {'doc_number': None} or {'doc_number': ''}: N/A

If document does not have a doc_number attribute: N/A

Example B: Formatting a Date and Applying HTML Escaping

{{ document.date | date('%Y-%m-%d') | e }}

Explanation: This expression processes the date attribute of the document variable.

date('%Y-%m-%d'): This custom filter (assuming it's defined or implicitly handled by the Jinja environment for datetime objects) formats the date into a "YYYY-MM-DD" string.

e (or escape): This built-in filter HTML-escapes the resulting string. This is crucial if the date string might contain characters that could be misinterpreted as HTML, preventing potential cross-site scripting (XSS) vulnerabilities.

Outcome:

If document.date is a datetime object representing May 27, 2024: 2024-05-27

If document.date is "2024-05-27" (and the date filter doesn't strip it): 2024-05-27<script>alert('XSS')</script> (The e filter makes it safe)

Example C: Capitalizing Document Type and Removing Tags

{{ document.doc_type | striptags | title }}

Explanation: This expression takes the doc_type attribute, applies the striptags filter (removes any HTML tags), and then applies the title filter (capitalizes the first letter of each word).

Outcome:

If document.doc_type is "purchase.order": Purchase Order

If document.doc_type is "invoice": Invoice

2. For Loop Example

This example demonstrates iterating over a list of items (e.g., line items within a document) and displaying their properties.

<h4>Document Line Items:</h4>

<ul>

{% for item in document.line_items %}

<li>{{ item.description | default("No Description") }{ - Qty: {{ item.quantity | default(0) }{ - Price: ${{ item.price | default(0.0) | round(2) }{</li>

{% else %}

<li>No line items found for this document.</li>

{% endfor %}

</ul>

Explanation: This is a for loop tag that iterates over a list of item objects (assuming document.line_items is a list passed to the template). For each item, it prints an <li> element displaying its description, quantity, and price, using default filters for robustness and round(2) for price formatting. The {% else %} block runs if the document.line_items list is empty.

Outcome:

Scenario 1: document.line_items is [{'description': 'Laptop', 'quantity': 1, 'price': 1200.50}, {'description': 'Mouse', 'quantity': 2, 'price': 25}]

<h4>Document Line Items:</h4>

<ul>

<li>Laptop - Qty: 1 - Price: $1200.50</li>

<li>Mouse - Qty: 2 - Price: $25.00</li>

</ul>

Scenario 2: document.line_items is [] (an empty list)

<h4>Document Line Items:</h4>

<ul>

<li>No line items found for this document.</li>

</ul>

Scenario 3: document.line_items contains items with missing data: [{'Keyboard'}, {'quantity': 5, 'price': 10.00}]

<h4>Document Line Items:</h4>

<ul>

<li>Keyboard - Qty: 0 - Price: $0.00</li>

<li>No Description - Qty: 5 - Price: $10.00</li>

</ul>

3. If Statement Example

This example uses if statements to conditionally display information based on document status or type.

{% if document.doc_status == 'Approved' %}

<p style="color: green;">This document has been Approved.</p>

{% elif document.doc_status == 'Pending' %}

<p style="color: orange;">This document is currently Pending review.</p>

{% elif document.doc_status == 'Rejected' %}

<p style="color: red;">This document has been Rejected. Please contact an administrator.</p>

{% else %}

<p>Document status: {{ document.doc_status | default("Unknown") }{.</p>

{% endif %}

{% if document.doc_type == 'Invoice' and document.reference_number %}

<p>For invoice number: {{ document.reference_number }{</p>

{% endif %}

Explanation: The first block is an if-elif-else statement that checks the value of document.doc_status. It displays a different message based on whether the document is 'Approved', 'Pending', 'Rejected', or any other status. The second if statement conditionally displays a reference number, but only if the doc_type is 'Invoice' AND a reference_number exists (is truthy).

Outcome:

Scenario 1: document is {'doc_status': 'Approved'}

<p style="color: green;">This document has been Approved.</p>

Scenario 2: document is {'doc_status': 'Pending'}

<p style="color: orange;">This document is currently Pending review.</p>

Scenario 3: document is {'doc_status': 'Rejected'}

<p style="color: red;">This document has been Rejected. Please contact an administrator.</p>

Scenario 4: document is {'doc_status': 'Draft'}

<p>Document status: Draft.</p>

Scenario 5: document is {'doc_type': 'Invoice', 'reference_number': 'INV-REF-007'} (Combined with a status example, e.g., 'Approved')

<p style="color: green;">This document has been Approved.</p>

<p>For invoice number: INV-REF-007</p>

Scenario 6: document is {'doc_type': 'Purchase Order', 'reference_number': 'PO-REF-123'} (The second if condition would be false because doc_type is not 'Invoice')

4. Macro Example

Macros allow defining reusable components. Here, a macro is used to generate a consistent display block for document properties.

Macro Definition:

{% macro display_document_property(label, value, default_value="N/A") %}

<div class="document-property">

<span class="label">{{ label }{:</span>

<span class="value">{{ value | default(default_value) }{</span>

</div>

{% endmacro %}

Explanation: This defines a macro named display_document_property. It takes three arguments:

label: The human-readable name of the property (e.g., "Document Number").

value: The actual variable or attribute whose value is to be displayed (e.g., document.doc_number).

default_value: An optional argument with a default of "N/A", used if value is undefined or empty. Inside the macro, it generates a <div> element with <span> tags for the label and value, applying the default filter to the value.

Outcome (of the definition): The macro itself doesn't produce output until called.

Macro Calls:

{{ display_document_property("Document Number", document.doc_number) }}

{{ display_document_property("Document Type", document.doc_type) }}

{{ display_document_property("Date", document.date | date('%Y-%m-%d')) }}

{{ display_document_property("Document Status", document.doc_status, "Undetermined") }}

{{ display_document_property("Reference Number", document.reference_number) }}

Explanation: These lines demonstrate calling the display_document_property macro.

  • The first two calls use the default "N/A" fallback.
  • The third call passes the document.date after applying a date filter for formatting.
  • The fourth call explicitly overrides the default_value to "Undetermined".

Outcome (of the calls):

Scenario 1: document is {'doc_number': 'ORD-567', 'doc_type': 'Order', 'date': datetime.date(2024, 1, 15), 'doc_status': 'Completed', 'reference_number': None}

<div class="document-property">

<span class="label">Document Number:</span>

<span class="value">ORD-567</span>

</div>

<div class="document-property">

<span class="label">Document Type:</span>

<span class="value">Order</span>

</div>

<div class="document-property">

<span class="label">Date:</span>

<span class="value">2024-01-15</span>

</div>

<div class="document-property">

<span class="label">Document Status:</span>

<span class="value">Completed</span>

</div>

<div class="document-property">

<span class="label">Reference Number:</span>

<span class="value">N/A</span>

</div>

Scenario 2: document is {'doc_number': None, 'doc_type': 'Quote', 'date': None, 'doc_status': 'Draft', 'reference_number': 'Q-001'}

<div class="document-property">

<span class="label">Document Number:</span>

<span class="value">N/A</span>

</div>

<div class="document-property">

<span class="label">Document Type:</span>

<span class="value">Quote</span>

</div>

<div class="document-property">

<span class="label">Date:</span>

<span class="value">N/A</span>

</div>