I'm a beginner at the flask, I want to practice filter, I've read the official documentation, but it doesn't help.
I want to create a firstof
filter, i.e., Outputs the first argument variable that is not “false” .(same as Django's firstof).
<h1>Hi, {{ "username, 'guest'"| firstof }} </h1>
@app.route('/')
def index():
return render_template('index.html') # expect output: Hi, guest
# return render_template('index.html', username='Carson') # Hi, Carson
I try,
@app.template_filter('firstof')
def firstof_filter(s: str):
"""
USAGE::
{{ "var1, var2, 'default_val'" | firstof }}
"""
for data in s.split(','):
data = data.strip()
if data[0] == data[-1] and data[0] in ("'", '"'):
return data[1:-1] # a constant
""" # does any similar grammar like below?
if hasattr(app.current.context, data):
return app.current.context[data]
"""
It would be nice if you could provide more links about flask-filter. thanks!
I found an answer by myself.
It isn't easy to achieve it with Flask directly.
because
# flask.templating.py
def render_template(template_name_or_list, **context):
ctx = _app_ctx_stack.top
ctx.app.update_template_context(context) # it does not store the context in the ctx. it just adds something to the context
return _render(
ctx.app.jinja_env.get_or_select_template(template_name_or_list),
# ctx.app.jinja_env is an environment (It combined Flask and Jinja, pretty like the Jinja.Environment)
# get_or_select_template, will run our function and no passing the context so that is why it so hard to do it.
context,
ctx.app,
)
def _render(template, context, app):
# It seems we can't do anything when the process already here.
before_render_template.send(app, template=template, context=context)
rv = template.render(context)
template_rendered.send(app, template=template, context=context)
return rv
But we get a clue that is we can change the Jinja.Environment
.
you may reference the Custom Filters first before you go ahead.
from flask import render_template
from jinja2 import contextfilter
from flask.helpers import locked_cached_property
from flask import Flask
from jinja2.runtime import Context
from flask.templating import Environment
from typing import Dict
from pathlib import Path
@contextfilter
def firstof_filter(ctx: Context, s: str):
ctx: Dict = ctx.parent
for data in s.split(','):
data = data.strip()
if data[0] == data[-1] and data[0] in ("'", '"'):
return data[1:-1] # a constant
if data in ctx:
return ctx[data]
class MyFlask(Flask):
@locked_cached_property
def jinja_env(self):
env: Environment = self.create_jinja_environment()
env.filters['firstof'] = firstof_filter # well, this way is not smart, you are better build a register to get all the filters that you have defined.
return env
app = MyFlask(__name__, template_folder=Path('./templates'))
@app.route('/')
def test():
return render_template('test.html', username='Carson') + '\n' + render_template('test.html')
app.run(debug=True)
where test.html
<h1>Hi, {{ "username, 'guest'"| firstof }} </h1>
p.s.
env.filters['firstof'] = firstof_filter
Well, this way is not smart. You are better to build a register to get all the filters that you have defined.
you can reference Django: django.template.library.py -> class Library
to collect all filters
...
Hi, Carson
Hi, guest