Skip to main content

Flask Template Filters

Introduction

When building web applications with Flask, you'll often need to transform or format data before displaying it in your HTML templates. Flask uses Jinja2 as its templating engine, which comes with built-in filters like lower(), upper(), and title() that modify variables directly in templates. But what if you need more specific transformations?

This is where Flask template filters come in. They allow you to create custom functions that transform data within your templates, keeping your presentation logic clean and maintainable. In this tutorial, we'll explore how to create and use custom filters in Flask applications.

Understanding Template Filters

Template filters in Flask are Python functions that transform data within your Jinja2 templates. They use the pipe symbol (|) syntax and can be applied to variables like this:

html
{{ variable_name | filter_name }}

Built-in Jinja2 Filters

Before creating custom filters, let's look at some useful built-in Jinja2 filters:

FilterDescriptionExample
capitalizeCapitalizes the first character{{ "hello" | capitalize }}Hello
lowerConverts to lowercase{{ "HELLO" | lower }}hello
upperConverts to uppercase{{ "hello" | upper }}HELLO
titleCapitalizes each word{{ "hello world" | title }}Hello World
trimRemoves whitespace from both sides{{ " hello " | trim }}hello
defaultUses a default value if variable is undefined{{ undefined_var | default('default value') }}default value
lengthReturns the length of a string or list{{ [1, 2, 3] | length }}3

Creating Custom Template Filters

Now, let's dive into creating your own custom filters. There are two main ways to register custom filters in Flask:

Method 1: Using app.template_filter() Decorator

python
from flask import Flask, render_template
from datetime import datetime

app = Flask(__name__)

# Create a custom filter to format dates
@app.template_filter('format_date')
def format_date_filter(date, format='%B %d, %Y'):
if isinstance(date, datetime):
return date.strftime(format)
return date

@app.route('/')
def index():
current_date = datetime.now()
return render_template('index.html', current_date=current_date)

In your template (index.html), you can use this filter like:

html
<p>Today's date: {{ current_date | format_date }}</p>
<p>Custom format: {{ current_date | format_date('%Y-%m-%d') }}</p>

Output:

Today's date: January 15, 2023
Custom format: 2023-01-15

Method 2: Using app.add_template_filter() Method

python
def reverse_string(text):
return text[::-1]

app.add_template_filter(reverse_string, 'reverse')

In your template:

html
<p>Original: {{ "Hello World" }}</p>
<p>Reversed: {{ "Hello World" | reverse }}</p>

Output:

Original: Hello World
Reversed: dlroW olleH

Practical Examples of Custom Filters

Let's explore some useful custom filters you might want to implement in your Flask applications:

1. Truncating Text with Ellipsis

python
@app.template_filter('truncate_text')
def truncate_text_filter(text, length=100):
if len(text) <= length:
return text
return text[:length] + '...'

Usage in template:

html
<p>{{ long_article | truncate_text(50) }}</p>

2. Converting Markdown to HTML

First, install the required package:

bash
pip install markdown

Then create a filter:

python
import markdown

@app.template_filter('markdown')
def markdown_filter(text):
return markdown.markdown(text)

Usage in template:

html
<div>{{ "# Hello\n\n**Bold text** and *italic*" | markdown | safe }}</div>

Note the safe filter that tells Jinja2 to render HTML tags rather than escaping them.

3. Currency Formatting

python
@app.template_filter('currency')
def currency_filter(value):
return f"${value:,.2f}"

Usage:

html
<p>Price: {{ 1234.56 | currency }}</p>

Output:

Price: $1,234.56

4. Time Ago Filter

python
from datetime import datetime

@app.template_filter('time_ago')
def time_ago_filter(dt):
now = datetime.now()
diff = now - dt

seconds = diff.total_seconds()

if seconds < 60:
return "Just now"
elif seconds < 3600:
minutes = int(seconds // 60)
return f"{minutes} minute{'s' if minutes > 1 else ''} ago"
elif seconds < 86400: # 24 hours
hours = int(seconds // 3600)
return f"{hours} hour{'s' if hours > 1 else ''} ago"
elif seconds < 604800: # 7 days
days = int(seconds // 86400)
return f"{days} day{'s' if days > 1 else ''} ago"
else:
return dt.strftime("%B %d, %Y")

Usage:

html
<p>Posted: {{ post_date | time_ago }}</p>

Passing Arguments to Filters

As you've seen in the examples above, you can pass arguments to custom filters:

html
{{ variable | filter_name(arg1, arg2) }}

In your filter function, these arguments come after the value being filtered:

python
@app.template_filter('filter_name')
def filter_function(value, arg1, arg2):
# Process value using arg1 and arg2
return result

Chaining Multiple Filters

Jinja2 filters can be chained together for more complex transformations:

html
{{ "HELLO WORLD" | lower | title }}

This will first convert "HELLO WORLD" to lowercase ("hello world") and then apply the title filter to get "Hello World".

Complete Example Application

Let's put everything together in a complete Flask application:

python
from flask import Flask, render_template
from datetime import datetime
import markdown

app = Flask(__name__)

# Date formatter filter
@app.template_filter('format_date')
def format_date_filter(date, format='%B %d, %Y'):
if isinstance(date, datetime):
return date.strftime(format)
return date

# Markdown filter
@app.template_filter('markdown')
def markdown_filter(text):
return markdown.markdown(text)

# Currency filter
@app.template_filter('currency')
def currency_filter(value):
return f"${value:,.2f}"

@app.route('/')
def index():
context = {
'current_date': datetime.now(),
'price': 1234.56,
'markdown_content': "# Welcome\n\nThis is **bold** and this is *italic*.",
'long_text': "This is a very long text that should be truncated in the template to show how the truncation filter works properly.",
}
return render_template('index.html', **context)

if __name__ == '__main__':
app.run(debug=True)

Template (templates/index.html):

html
<!DOCTYPE html>
<html>
<head>
<title>Flask Template Filters Example</title>
</head>
<body>
<h1>Flask Template Filters Example</h1>

<h2>Date Filter</h2>
<p>Default format: {{ current_date | format_date }}</p>
<p>Custom format: {{ current_date | format_date('%Y-%m-%d') }}</p>

<h2>Currency Filter</h2>
<p>Price: {{ price | currency }}</p>

<h2>Markdown Filter</h2>
<div>{{ markdown_content | markdown | safe }}</div>

<h2>Built-in Filters</h2>
<p>Lowercase: {{ "HELLO WORLD" | lower }}</p>
<p>Title case: {{ "hello world" | title }}</p>
<p>Length: {{ [1, 2, 3, 4] | length }}</p>

<h2>Chaining Filters</h2>
<p>{{ "HELLO WORLD" | lower | title }}</p>
</body>
</html>

Best Practices for Template Filters

  1. Keep filters focused: Each filter should do one thing well, following the single responsibility principle.

  2. Use appropriate names: Choose clear, descriptive names for your filters.

  3. Separate logic from presentation: Filters should handle presentation logic, not core business logic.

  4. Document your filters: Add docstrings to your filter functions to help other developers understand what they do.

  5. Cache complex filters: If your filter does heavy processing, consider caching the results.

  6. Handle edge cases: Make sure your filters gracefully handle unexpected inputs.

Summary

Flask template filters provide a powerful way to transform and format data within your templates. They help keep your presentation logic clean and maintainable by:

  • Allowing you to create reusable transformation functions
  • Enabling data formatting directly in templates
  • Separating presentation logic from business logic
  • Providing built-in functionality through Jinja2's filter system
  • Supporting customization through the creation of application-specific filters

By mastering template filters, you'll be able to create more dynamic and user-friendly Flask applications with cleaner, more maintainable template code.

Exercises

  1. Create a filter that takes a list of dictionaries and sorts them based on a specified key.
  2. Implement a filter that converts newline characters (\n) to HTML <br> tags.
  3. Create a filter that takes a URL and returns just the domain name.
  4. Build a filter that pluralizes English words (e.g., "1 item" vs "2 items").
  5. Create a filter that formats file sizes from bytes to KB, MB, GB, etc.

Additional Resources

Happy coding!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)