Search code examples
htmlflaskjinja2forexpython-babel

How to dynamically switch jinja filter


I am currently making a web app that displays a list of transactions of the user. I want to make a setting to change the currency from USD to EUR. I am using custom filter in jinja:

My custom filter and function:

from forex_python.converter import CurrencyRates
from babel.numbers import format_currency

c = CurrencyRates()

# Format and convert transaction amount in EUR
def to_euros(value):
    value = c.convert('USD', 'EUR', value)
    return format_currency(value, 'EUR', locale='en_US')
app.jinja_env.filters['to_euros'] = to_euros

# Format transaction amount in USD
def to_usd(value):
    value = c.convert('USD', 'USD', value)
    return format_currency(value, 'USD', locale='en_US')
app.jinja_env.filters['to_usd'] = to_usd

My routes.py:

@app.route("/history", methods=['GET', 'POST'])
@login_required
def history():
    # Getting all transactions linked to user id
    row = Transactions.query.filter_by(user_id=current_user.id).order_by(Transactions.date.desc())
    return rnd_tmp("history.html", rows=row)

And in my html file:

{% extends "layout.html" %}

{% block title%}
    History
{% endblock %}

{% block main %}
    <div id="body-main">
        <table class="table table-striped table-full">
            <thead>
                <th>Date</th>
                <th>Type</th>
                <th>Category</th>
                <th>Amount</th>
                <th>Notes</th>
            </thead>
            <tbody>
                {% for row in rows %}
                    <tr>
                        <td>{{ row['date'] }}</td>
                        <td>{{ row['type'] }}</td>
                        <td>{{ row['category'] }}</td>
                        <td>{{ row['amount'] | to_usd }}</td>
                        <td>{{ row['notes'] }}</td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
{% endblock %}

I want to make a new route that has a select option to change between USD and EUR currency, so changing from <td>{{ row['amount'] | to_usd }}</td> to <td>{{ row['amount'] | to_euros }}</td>. How can I do that dynamically using Flask?


Solution

  • Jinja filters can take arguments - see the documentation. This means that you can write a more generic to_currency filter that can take the selected currency as a parameter.

    For example:

    def to_currency(value, code):
        value = c.convert('USD', code, value)
        return format_currency(value, code, locale='en_US')
    
    app.jinja_env.filters['to_currency'] = to_currency
    

    And you would use it in the following manner in a Jinja template:

    <td>{{ row['amount'] | to_currency(selected_currency) }}</td>
    

    The string value selected_currency would be passed in from the route either as 'USD' or 'EUR'.

    See diagram below showing where the left side of the filter 'the value' gets passed into the Python function along with the filter's arguments:

    enter image description here