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:
{{ variable_name | filter_name }}
Built-in Jinja2 Filters
Before creating custom filters, let's look at some useful built-in Jinja2 filters:
Filter | Description | Example |
---|---|---|
capitalize | Capitalizes the first character | {{ "hello" | capitalize }} → Hello |
lower | Converts to lowercase | {{ "HELLO" | lower }} → hello |
upper | Converts to uppercase | {{ "hello" | upper }} → HELLO |
title | Capitalizes each word | {{ "hello world" | title }} → Hello World |
trim | Removes whitespace from both sides | {{ " hello " | trim }} → hello |
default | Uses a default value if variable is undefined | {{ undefined_var | default('default value') }} → default value |
length | Returns 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
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:
<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
def reverse_string(text):
return text[::-1]
app.add_template_filter(reverse_string, 'reverse')
In your template:
<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
@app.template_filter('truncate_text')
def truncate_text_filter(text, length=100):
if len(text) <= length:
return text
return text[:length] + '...'
Usage in template:
<p>{{ long_article | truncate_text(50) }}</p>
2. Converting Markdown to HTML
First, install the required package:
pip install markdown
Then create a filter:
import markdown
@app.template_filter('markdown')
def markdown_filter(text):
return markdown.markdown(text)
Usage in template:
<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
@app.template_filter('currency')
def currency_filter(value):
return f"${value:,.2f}"
Usage:
<p>Price: {{ 1234.56 | currency }}</p>
Output:
Price: $1,234.56
4. Time Ago Filter
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:
<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:
{{ variable | filter_name(arg1, arg2) }}
In your filter function, these arguments come after the value being filtered:
@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:
{{ "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:
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
):
<!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
-
Keep filters focused: Each filter should do one thing well, following the single responsibility principle.
-
Use appropriate names: Choose clear, descriptive names for your filters.
-
Separate logic from presentation: Filters should handle presentation logic, not core business logic.
-
Document your filters: Add docstrings to your filter functions to help other developers understand what they do.
-
Cache complex filters: If your filter does heavy processing, consider caching the results.
-
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
- Create a filter that takes a list of dictionaries and sorts them based on a specified key.
- Implement a filter that converts newline characters (
\n
) to HTML<br>
tags. - Create a filter that takes a URL and returns just the domain name.
- Build a filter that pluralizes English words (e.g., "1 item" vs "2 items").
- Create a filter that formats file sizes from bytes to KB, MB, GB, etc.
Additional Resources
- Jinja2 Template Designer Documentation
- Flask Documentation on Templates
- Python
datetime
module documentation - List of useful custom Jinja2 filters
Happy coding!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)