I have a flask application with multiple functions that I would like to return the same arguments to create a navbar that shows the user's information. I'm making a navbar that's similar to the top navigation bar from StackOverflow - whichever page you visit on this site, it shows the user's profile picture, reputation, number of badges, etc.
I would like to implement this functionality in my flask application. When the user logs in, I will store the user_info in a flask session. However, it appears that I have to repeat the same process of checking if "user_info" exists in session for every single function, so that for each page the user visits, the navbar will be rendered correctly.
@app.route('/')
def index():
...
if "user_info" in session:
return render_template("index.html", user_info=session["user_info"], data=data)
else:
return render_template("index.html", data=data)
@app.route('/info')
def info():
...
if "user_info" in session:
return render_template("index.html", user_info=session["user_info"], data=data, info=info)
else:
return render_template("index.html", data=data, info=info)
In HTML I created a parent Jinja template with the navbar, and each child page (index and info) inherits the navbar.
Jinja Parent Template:
<nav>
{% if user_info is defined %}
<li>
<img class="rounded-circle profile" width="30" src="{{ user_info['picture'] }}" alt="User">
</li>
<li>
<span>{{ username }}</span>
</li>
{% else %}
<li class="nav-item">
<a href="/login"><button>Sign In</button></a>
</li>
{% endif %}
</nav>
Is it possible to simplify this, so that I wouldn't have to write the same code for each endpoint?
EDIT: based on @arsho's answer, I returned None if the user is not logged in, and in Jinja I changed the if-statement to {% if user_info is not none %} - but I would still like to simplify the repeated render_template(user_info=session.get("user_info", None), data=data) for each function, is there a way to achieve this? Thanks in advance!
UPDATE: I finally figured it out! The templating decorator from the flask documentation helped me a lot!
What I did was I created their sample templated function and made a slight modification to return the specific arguments I need for my application:
def templated(template=None):
def decorator(f):
@functools.wraps(f)
def decorated_function(*args, **kwargs):
template_name = template
if template_name is None:
template_name = request.endpoint \
.replace('.', '/') + '.html'
ctx = f(*args, **kwargs)
if ctx is None:
ctx = {}
elif not isinstance(ctx, dict):
return ctx
return render_template(template_name,
data=data, # added argument
user_info=session.get('user_info', None), # added argument
**ctx)
return decorated_function
return decorator
and for each function, I simply added the view decorator templated('[TEMPLATE_NAME]') and returned a dict object for the additional arguments I would like to return, and it worked perfectly! Hope this would also help others with the same issue.
Now the endpoints look like:
@app.route('/')
@templated('index.html')
def index():
...
# No need to return any additional arguments - already specified in the templated view decorator
@app.route('/info')
@templated('info.html')
def info():
...
return dict(info=info) # Additional argument: info; user_info and data already included in view decorator
Jinja Parent Template:
<nav>
{% if user_info is not none %}
<li>
<img class="rounded-circle profile" width="30" src="{{ user_info['picture'] }}" alt="User">
</li>
<li>
<span>{{ username }}</span>
</li>
{% else %}
<li class="nav-item">
<a href="/login"><button>Sign In</button></a>
</li>
{% endif %}
</nav>


