Access child variable from an include in parent template - jinja2

I'm trying to specify a variable in a child template and access it from another template which is included in the parent template.
Here's what I'm trying to do (code trimmed to the bare minimum):
child.html
{% set var = 'foo' %}
{% extends 'base.html' %}
base.html
{% include 'bar.html' %}
bar.html
{{ var }}
At this point, I render the child.html file and nothing is output.
This is what I've tried:
If I specify the variable in the base.html file instead of the child.html file, it works.
If I pass the variable in my render call, it works. template.render(var = 'foo')
Chaging the base.html file to be {% include 'bar.html' with context %} doesn't fix it.
I am able to access the variable in my base.html file just fine, and in fact, I've managed to create a workaround by adding {% set foo = var %} to my base.html, and changing bar.html to {{ foo }}
So concisely put, my question is: Is there a way for a template that's included in a parent template to access a variable set in a child template? (Without having to define a new variable in the parent file like my workaround)

I was able to get this by passing the variable within the child block on the parent template.
Ex. I have active_page declared on my child template pricing.html.
At first, I was not able to access active_page within my included file navbar-alt.html but when I set the variables inside of the navbar block it worked.
{% block wrapper %}
<div id="wrapper">
{% block navbar %}
{% set navigation_bar = [
('/what-we-do', 'what-we-do', 'what we do'),
('/pricing', 'pricing', 'pricing'),
('/demo', 'demo', 'demo'),
('http://indico.readme.io/v1.0/docs', 'docs', 'docs')
] -%}
{% set active_page = active_page|default('index') -%}
{% if "dashboard" not in request.path %}
{% include 'includes/navbar-alt.html' %}
{% else %}
{% include 'includes/navbar.html' %}
{% endif %}
{% endblock navbar %}
{% if "dashboard" in request.path %}
{% block sidebar %}
{% include 'includes/sidebar.html' %}
{% endblock sidebar %}
{% endif %}
{% block content_wrapper %}
<div id="content">
{% block content %}{% endblock content %}
</div>
{% endblock content_wrapper %}
{% include 'includes/flashes.html' %}
</div>
{% endblock wrapper %}
Original inspiration:
Jinja Tricks

Related

What is the "base" file that the Bokeh index.html extends?

I am a little confused about what {% extends base %} is extending at the start of index.html in Bokeh server application packages.
Examples of this can be seen in:
Bokeh Docs: Embedding in Templates
{% extends base %}
{% block contents %}
<div>
<p> Hello {{ user_id }}, AKA '{{ last_name }}, {{ first_name }}'! </p>
</div>
{% endblock %}
Bokeh Server Application Examples
Example code from the Gapminder package in templates/index.html
{% extends base %}
{% block title %}Bokeh Gapminder Example{% endblock %}
{% block postamble %}
<style>
{% include 'styles.css' %}
</style>
{% endblock %}
What is this "base" that is being extended?
I see that there is a "contents" block, "title" block, and "postamble" block from the above examples.
How do I know what other jinja blocks I can modify?
Thanks.

Jinja Scope on Inherited Template

I am developing a website using Flask on the back-end and Jinja for templating. Every page should have the same image as part of the Open Graph Protocol, except for one of them, where I want to customize the url with something peculiar to that page.
The child template (like all other templates) extends base, but only this one will ever have a value set for the variable ob_image_url, therefore I need to check if that value exists and if not set a default value.
When the child page.html template is rendered, I am always getting the default value and not the one for that page. Where am I messing it up?
Thanks!
base.html
{% block og_image %}{% endblock %}
{% if not og_image_url %}
{% set og_image_url = url_for('static', filename='img/logo.png', _external=True) %}
{% endif %}
<meta property="og:image" content="{{og_image_url}}" />
page.html
{% extends "base.html" %}
{% block og_image %}
{% if obj and obj.image %}
{% set og_image_url = obj.image %}
{% endif %}
{% endblock %}

Idiomatic way in Jekyll to inherit default data if project specific data unavailable

I am completely new to Jekyll. I did something like this:
{% assign top_nav = site.data.menus %}
{% if site.data.orgs[site.orgData].menus %}
{% assign top_nav = site.data.orgs[site.orgData].menus %}
{% endif %}
<ul>
{% for menu in top_nav %}
<li>
{{ menu.title }}
</li>
{% endfor %}
</ul>
Basically, I will grab an array of navigation items from a default folder. But if I notice the existence of a menu for a specific organization, then I will override the menu provided by the default folder.
What I don't like about this approach is I now have hundreds of places in my jekyll templates that does this if statement check. If this were any other scripting programming language, I would define a function like function($org_name,$prop) {return $site.data.orgs[$org_name][$prop] ? $site.data.orgs[$org_name][$prop] : $site.data[$prop]; } . What would be the idiomatic way to achieve the same objective in jekyll?
I tried a variation of David Jacquel's suggestion by doing this
./org_var.html
{% assign prop = include.prop %}
{% assign orgVar = site.data[prop] %}
{% if site.data.orgs[site.orgData][prop] %}
{% assign orgVar = site.data.orgs[site.orgData][prop] %}
{% endif %}
./_include/nav.html
{% include_relative ../org_var.html prop=menus %}
{% for menu in orgVar %}
... print menu items
./_layout/header.html
{% include_relative ../org_var prop='electronics.televisions' %}
{% for tv in orgVar%}
{{ tv.modelName }}
... print tv values
{% endfor %}
But I get a syntax error in ../org_var.html saying {% include_relative file.ext param='value' param2='value' %} . The documentation says I can't use relative path with include or include_relative. How do I make my org_var.html a reusable and global function? And will electronics.televisions even evaluate properly to the proper path of my site.data.org.[site.orgData][...path] variable?
Just realized there is a default: modifier for a variable like smarty templates.
{% assign top_nav = site.data.orgs[site.orgData].menus | default: site.data.menus %}
You can use Jekyll includes.
From anywhere you want to use your include :
{% include nav.html org_name=org_name prop=prop %}
Will call _include/nav.html that can be something like this :
{% assign org_name = include.org_name %}
{% assign prop = include.prop %}
{% if site.data.orgs[org_name][prop] %}
{% assign top_nav = site.data.orgs[site.orgData].menus %}
{% else %}
{% assign top_nav = site.data.orgs[site.orgData].menus %}
{% endif %}
<ul>
{% for menu in top_nav %}
...

How to add a background using CSS in my Flask app.?

I am new to Flask and trying to add a background image to all of the pages of my web app. I have saved the background image as 'background-image.jpg' in an 'images' folder within a 'static' folder, in the project. Here is my 'base.html' file which is extended to all my other files.
{% extends 'bootstrap/base.html' %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/custom-styles.css') }}">
{% block title %}
Flog
{% endblock %}
{% block head %}
{{ ckeditor.load() }}
{{ super() }}
{% endblock %}
{% block navbar %}
{% include '_navbar.html' %}
{% endblock %}
{% block content %}
<div class="jumbotron background-img">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}" role="alert">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block sub_content %}
{% endblock %}
</div>
{% endblock %}
And here is my css file, saved in a 'css' folder within the 'static' folder named 'custom-styles.css'.
.background-img{
background-image: url('..images/background-image.jpg');
}
When I run the app, the jumbotron is working but it just shows a grey box and no background image. Is anybody able to tell me what I am doing wrong?
Thanks.
.background-img{
background-image: url('../images/background-image.jpg');
}
Try adding the forward slash after the two periods, it looks like it might just be an issue with how the css file is accessing the images folder.
The path to your background image needs to be correct. If that file is a static file then you need the static file path for the background image.
For example, if the path for background-image.jpg is:
path-to-static/images/background-image.jpg
Then instead of ..images/background-image.jpg you would need to use:
.background-img{
background-image: url('/static/images/background-image.jpg');
}
That is, I believe, the default path to static files, though you can make configuration changes to that location.

Tag 'use' of twig templating system duplicate content

Using the sf2.6.4 and the Twig templating system, I have a weird behaviour with the tag 'use'.
With a simple base template defining a default navbar and header content :
{# base.html.twig #}
<html lang="fr">
<head></head>
<body>
{% block navbar %}<hr> nav bar foo bar<hr>{% endblock %}
<!-- a default header -->
{% block header %}
{% include 'AppBundle:TestingTwigUse:header.html.twig' %}
{% endblock %}
</body>
With the given default header.html.twig template :
{# header.html.twig #}
{% block header_container %}
{% block header_title %}<h1>Default title</h1>{% endblock %}
{% block header_content %}
<div>
This default text header, blablabla...
</div>
{% endblock %}
{% endblock %}
When I try to build an index template, inheriting the base one and overiding the header content using 'use' tag like following :
{# index.html.twig #}
{% extends "AppBundle:TestingTwigUse:base.html.twig" %}
{% block header %}
{% use 'AppBundle:TestingTwigUse:header.html.twig' %}
{% block header_container %}
{{ parent() }}
{% block header_content %}
***** child template specific content ****
{% endblock %}
{% endblock %}
{% endblock %}
I have the strange following result with header_content block twice :
***** child template specific content **** ***** child template specific content ****
any idea ?
I thinkg the problem is you have two header_content blocks - one in the index.twig.html, second in the parent() call. Second definition overrides first which leads to duplications after compilation.
If you want to override the default text - you should remove parent() call. If you want to update (append) the default text - you should refactor your blocks structure to avoid identical names.
UPD Try following index
{# index.html.twig #}
{% extends "AppBundle:TestingTwigUse:base.html.twig" %}
{% block header_content %}
***** child template specific content ****
{% endblock %}
UPD 2 Check this solution
I found a solution. Use the block() function to get the child's block
content and pass it to header.html.twig as a variable in the include
statement
Finaly two solutions :
With 'use', we can get the part we want to keep but loosing inheritance :
{% block header %}
{% use 'AppBundle:TestingTwigUse:header.html.twig' with header_title as parent_header_title %}
{% block header_title %}
{{ block('parent_header_title') }}
{% endblock %}
***** child template specific content ****
{% endblock %}
The one is 'embeb' that combines include AND extends behaviour :
{% block header %}
{% embed 'AppBundle:TestingTwigUse:header.html.twig' %}
{% block header_content %}
***** child template specific content ****
{% endblock %}
{% endembed %}
{% endblock %}