what are the differences between import and extends in Flask? - python

I am reading 《Flask web development》.
in Example 4-3,
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
I'd like to know:
What are the differences between extends and import?(I think they are quite similar in usage.)
In which situation,I will use extends or import?

When you extend another template the template controls you (the called controls the caller) - only named blocks in the "parent" template will be rendered:
{% extends "base.html" %}
{% block main_content %}
Only shows up if there is a block called main_content
in base.html.
{% endblock main_content%}
On the other hand an import simply binds the template to a name in your template's scope, and you control when and where to call it (the caller controls the called):
{% import "bootstrap/wtf.html" as wtf %}
Some of your own template code with {{ wtf.calls() }} where it makes sense.

There is a difference. {% extends parent.html %} allows you to render parent.html and also override {% block %}'s defined in it while {% import %} just allows you to access template variables.
So example template is extends base.html and imports variables from bootstrap/wtf.html. Think about it like python's class inheritance and import statement.

By default, included templates are passed the current context and imported templates are not.
Jinja documentation
By default will the included templates not be cached while the imported will.
The reason for this is that imports often are used as a module that holds macros.
The best practice will be to use import on templates that contain macro's while include is best to use when you just wan't some markup template.

Related

How to avoid rendering a block in a base template - or: how to define a multiline block-like variable?

I am rather new to jinja, so please excuse this potentially dumb question ...
I have a jinja macro sitting in a file named lib.jinja which looks as as follows
{% macro some_macro(some_content) %}
<div class="some_class">{{ some_content }}</div>
{%- endmacro %}
I import it from another file named content.htm, which looks roughly as follows:
{% from "lib.jinja" import some_macro %}
{% block content %}
Some content
{% endblock %}
{{ some_macro(self.some_macro(self.content())) }}
The rendered output looks as follows:
Some content
<div class="some_class">Some content</div>
Notice that the content appears twice, once rendered through/by the macro, another time - directly above - rendered like it was defined in the block named content in content.htm. I would love to avoid that the block is rendered twice - it is supposed to be fed into the macro and rendered through it only.
I appears that I can "work around" it by turning content.htm into a child template (by adding an extends tag at its top) of an empty base template.
Maybe I have a lack of understanding for the concepts here: A block is not a variable - I get that. If there is no extends tag, the template is treated as a base template, I assume. If there are no child templates or other directives, I guess the block is just rendered as-is and then, for a second time so to speak, picked up by the macro - correct?
How can I solve this in a sane manner, i.e. how could I handle this other than by extending an empty base template? Is there another type of block or variable that would fit my needs?
Just after I submitted this question, I finally found what I was looking for in the manual: Block Assignments.
{% from "lib.jinja" import some_macro %}
{% set content %}
Some content
{% endset %}
{{ some_macro(self.some_macro(content)) }}
The set and endset tags solve my problem.

Invalid block tag on line 5: 'else'. Did you forget to register or load this tag? [duplicate]

I would like to have a template that extends another conditionally. Basically, when a variable called "ajax" is true I DO NOT want to have the template extend another.
{% if not ajax %}
{% extends "/base.html" %}
{% endif %}
Any clues?
While you may not wrap extends in logic blocks, since it must be the first tag if used, it can still accept variables, including filters. This should fit your purpose nicely:
{% extends ajax|yesno:"base_ajax.html,base.html" %}
{# stuff #}
Note: the yesno filter also accepts null values (None) as the third choice, and if you do not specify one (as in this case), it will fallback to converting it to False (i.e. it will return the second choice). This allows you to not specify the ajax variable in your template without breaking it.
Suggested by user Rafael:
{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %}
{# stuff #}
This will only work if you are using a RequestContext context instead of a plain Context object and you have the request context processor enabled, or alternatively, if you insert the request object in your template context.
You cannot do it like that. You can however set a variable and use that to choose the template to extend:
{% extends my_template %}
Then in python code you write something like:
if ajax:
template_values['my_template'] = 'base_ajax.html'
else:
template_values['my_template'] = 'base.html'
You may wish to refer to the documentation for more information.
I was looking for the solution of the same problem and came with a bit better workaround than suggested by Klaus Byskov Hoffmann. It is better because you don't have to have 2 separate base templates for ajax and non-ajax requests and, which is more important, you don't have to define if statement that will define which base template to use in EACH controller.
In your case the solution would be:
page.html
{% extends "/base.html" %}
{% block body %}
hello world
{% endblock body %}
base.html
{% if not ajax %}
<html>
<head></head>
<body>
LOGO and other stuff...
{% endif %}{% block body %}{% endblock body %}{% if not ajax %}
FOOTER
</body>
</html>
{% endif %}
So, base.html is always included but it prints its content only when not ajax.
UPDATE: This can be simplified by creating and adding a new ConextProcessor that will populate ajax context variable from the HttpRequest.is_ajax(), so you don't have to do anything extra in your controllers and templates at all.
If you don't want to "extend" a parent template, you can always create an empty file called null.html and extend that. Kind of hacky, but easy to understand.
I know this is an old thread but in case someone stumbles across this, I believe the best way to achieve your goal would be:
"views.py"
if ajax:
request.session['ajax'] = 'ajax'
"base.html"
{% if 'ajax' in request.session %}
{% include "ajax.html" %}
{% endif %}
and in "ajax.html" you wouldn't use any {% extends %} or {% block %} tags since you won't be calling anything for "ajax.html" to extend onto the base. That will be taken care of by "include".

Is it possible to load a custom template tag in base and use it in extented templates

I loaded a custom template tag note_extras.py in base.html.
base.html
<div id="wrap">
{% load note_extras %}
{% block content %}
{% endblock %}
</div><!--wrap-->
but it is not accessible at templates which is an extend of base.html ie::
home.html
{% extends "base.html" %}
{% block content %}
<div class="container">
{% create_tagmenu request.user.pk %}
</div>
{% endblock %}
it is working fine if i load note_extras in home.html ie:
{% extends "base.html" %}
{% load note_extras %}
....
In Django template language, you must load all needed template libraries in each of the templates.
I personally think it is a good idea because it makes templates more explicit (which is better than implicit). Ill give an example. Prior to Django 1.5, the default behavior for a url tag was to provide the view name in plaintext as well as all the needed parameters:
{% url path.to.view ... %}
There however was no way to provide the path to the view via a context variable:
{% with var='path.to.view' %}
{% url var ... %}
{% endwith %}
To solve that, starting with 1.3, you could import the future version of the url tag (which became the default in 1.5) by doing:
{% load url from future %}
{% url var ... %}
or
{% url 'path.to.view' ... %}
Now imagine that you would need to create a template which would extend from a base template which you did not create (e.g. one of django admin base templates). Then imagine that within the base template it would have {% load url from future %}. As a result, {% url path.to.view ... %} within your template would become invalid without any explicit explanation.
Of course this example does not matter anymore (starting with 1.5) however hopefully it illustrates a point that being explicit in templates is better than implicit which is why the currently implementation is the way it is.
If you want that a template tag is loaded in every template you want to do it in the init file of your app:
from django.template.loader import add_to_builtins
add_to_builtins('my_app.templatetags.note_extras')
In case anyone was wondering, add_to_builtins has been deprecated but one could still load a tag for all of the templates in the project via settings.TEMPLATES - supported for Django 1.9 onwards as described here:
https://stackoverflow.com/a/59719364/2447803

Python / Django - If statement in template around extends

I would like to have a template that extends another conditionally. Basically, when a variable called "ajax" is true I DO NOT want to have the template extend another.
{% if not ajax %}
{% extends "/base.html" %}
{% endif %}
Any clues?
While you may not wrap extends in logic blocks, since it must be the first tag if used, it can still accept variables, including filters. This should fit your purpose nicely:
{% extends ajax|yesno:"base_ajax.html,base.html" %}
{# stuff #}
Note: the yesno filter also accepts null values (None) as the third choice, and if you do not specify one (as in this case), it will fallback to converting it to False (i.e. it will return the second choice). This allows you to not specify the ajax variable in your template without breaking it.
Suggested by user Rafael:
{% extends request.is_ajax|yesno:"base_ajax.html,base.html" %}
{# stuff #}
This will only work if you are using a RequestContext context instead of a plain Context object and you have the request context processor enabled, or alternatively, if you insert the request object in your template context.
You cannot do it like that. You can however set a variable and use that to choose the template to extend:
{% extends my_template %}
Then in python code you write something like:
if ajax:
template_values['my_template'] = 'base_ajax.html'
else:
template_values['my_template'] = 'base.html'
You may wish to refer to the documentation for more information.
I was looking for the solution of the same problem and came with a bit better workaround than suggested by Klaus Byskov Hoffmann. It is better because you don't have to have 2 separate base templates for ajax and non-ajax requests and, which is more important, you don't have to define if statement that will define which base template to use in EACH controller.
In your case the solution would be:
page.html
{% extends "/base.html" %}
{% block body %}
hello world
{% endblock body %}
base.html
{% if not ajax %}
<html>
<head></head>
<body>
LOGO and other stuff...
{% endif %}{% block body %}{% endblock body %}{% if not ajax %}
FOOTER
</body>
</html>
{% endif %}
So, base.html is always included but it prints its content only when not ajax.
UPDATE: This can be simplified by creating and adding a new ConextProcessor that will populate ajax context variable from the HttpRequest.is_ajax(), so you don't have to do anything extra in your controllers and templates at all.
If you don't want to "extend" a parent template, you can always create an empty file called null.html and extend that. Kind of hacky, but easy to understand.
I know this is an old thread but in case someone stumbles across this, I believe the best way to achieve your goal would be:
"views.py"
if ajax:
request.session['ajax'] = 'ajax'
"base.html"
{% if 'ajax' in request.session %}
{% include "ajax.html" %}
{% endif %}
and in "ajax.html" you wouldn't use any {% extends %} or {% block %} tags since you won't be calling anything for "ajax.html" to extend onto the base. That will be taken care of by "include".

Django template: Why block in included template can't be overwritten by child template?

To illustrate my question more clearly, let's suppose I have a include.html template with content:
{% block test_block %}This is include{% endblock %}
I have another template called parent.html with content like this:
This is parent
{% include "include.html" %}
Now I create a templated called child.html that extends parent.html:
{% extends "parent.html" %}
{% block test_block %}This is child{% endblock %}
My idea is that when rendering child.html, the test_block in child.html can overwrite the one in include.html. As per my understanding, when a template is included, it is included as it is. So in my case, I think parent.html equals to:
This is parent
{% block test_block %}This is include{% endblock %}
So child.html should be able to overwrite test_block. But looks like it can't. Why? Is there a workaround?
When you include a template, it renders the template, then includes the rendered content.
From the django docs:
The include tag should be considered as an implementation of "render this subtemplate and include the HTML", not as "parse this subtemplate and include its contents as if it were part of the parent". This means that there is no shared state between included templates -- each include is a completely independent rendering process.
A workaround would be to have the child template extend the included template instead of the including template. Then, include the child template.

Categories