How to extend django template from python string - python

I have a string that looks like
string '''
<html>
<head>
{% block head %}{% endblock %}
... other stuff ...
</head>
<body>
{% block body %}{% endblock %}
... other stuff ...
</body>
</html>
'''
I would like the following django template to inherit from the above string:
{% block head %}
... other stuff ...
{% endblock %}
{% block body %}
<h1>Other stuff</h1>
{% endblock %}
Since the string is not in a file a can't just specify it's filename to the template rendering engine. Any ideas?

In order to implement string-only template extender, you would probably have to implement your template loader. Actually, a much cleaner solution is to use threed's suggestion below. Pass a parent Template() in the context rather than the name of disk template:
>>> from django.template import Context, Template
>>> extend_me = Template("Before A. {% block a %}{% endblock %}. After A")
>>> str_template = "{% extends parent %} {% block a %}This is inside A{% endblock %}"
>>> Template(str_template).render(Context({"parent": extend_me}))
u'Before A. This is inside A. After A'
Unfortunately this doesn't seem to work on django 1.3, possibly due to bug #7377 (you can't use the extends and block tags in string templates). Though it does work fine in 1.2. So if you happen to be running 1.3, you can look up the history of this question and use my hack :)
The extends template tag allows you to specify a variable name (since ver 1.0).
There's an example of it's usage in this question: How do I use Django's template extends variable?

It turns out there's an even simpler way to achieve this:
from google.appengine.ext.webapp import template
parent = template.Template("<html><body>{% block content %}{% endblock %}</body></html>")
path = os.path.join(os.path.dirname(__file__), 'index.html')
template.render(path, template.Context({"baseTemplate": parent}))
Where the index.html file looks like this:
{% extends baseTemplate %}
{% block content %}
<h1>Hello World!</h1>
{% endblock %}
In this case, the template object (as opposed to just a string) is passed into the context of the child template and used as the variable in the 'extends' tag.

Related

Django Admin multiple template inheritance

I thought it was a quite simple task but turns out it's not.
So, I have two different mixins to use on a Django admin class. they both have some codes in it with templates.
admin.py
class AdminMixin01(admin.ModelAdmin):
change_form_template = "change_form1.html"
class AdminMixin02(admin.ModelAdmin):
change_form_template = "change_form2.html"
class ModalAdmin(AdminMixin01, AdminMixin02, admin.ModelAdmin):
pass
change_form1.html
{% extends "change_form.html" %}
{% block content %}
{{ block.super }}
Form 1
{% endblock content %}
change_form2.html
{% extends "change_form.html" %}
{% block content %}
{{ block.super }}
Form 2
{% endblock content %}
it's looks quite simple both python and html sides.
The problem is Django renders only first mixin's template and ignores the second mixin's template. In this case only change_form1.html rendered into original change_form.html template and no traces from change_form2.html.
The python codes in the both mixin are working except html codes.
Any ideas ?

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".

In Jinja2, how can I use macros in combination with block tags?

I'm a front end developer, and I've been trying to get a hang on using Jinja2 effectively. I want to tweak a current site so it has multiple base templates using inheritance, it fully uses block tags to substitute content and override it, and uses macros to support passing of arguments.
My base template contains this code (edited for simplicity):
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
{% from "foo.html" import macro1, macro2, macro3 %}
{% macro base_template(title=none, arg2=none, urls={}, arg3=false) %}
<html>
<title>{{ title }} | Site.com</title>
....
{{ caller() }}
....
</html>
{% endmacro %}
{% block content %}{% endblock %}
And my pages that extend it look like this:
{% extends "base.html" %}
{% block content %}
{% call base_template(title="home", arg2="active", arg3="true") %}
(html code here)
{% endcall %}
{% endblock %}
So basically all the pages extend base, they call a macro and pass arguments to that macro. I don't quite understand it all, but the main point is that this allows default values and a degree of flexibility that doesn't require redefining an entire block: it gives some degree of flexibility and power. Again this is heavily simplified.
The only problem is, this negates my ability to use blocks. Macros are for flexibility, but with blocks, I have the ability to override something entirely, or use it's parents contents and add to it, which I can't do with Macros (at least I don't think). The problem is, I can't wrap things in blocks, else they won't see the values in the macro. For instance, doing this:
{% block title %}<title>{{ title }} | Site.com</title>{% endblock %}
Will fail because it will say title is undefined.
Ultimately I am looking for a way to utilize both the power and organiztional aspects of blocks, but still be able to utilize the logic & terseness of macros. If anyone could give me any help as to how I might go about this problem, I would really appreciate it.
Blocks are only definable at a template's top level. If you extend a template, any values set in the child template using a set tag will be accessible from the template it is extending. For example, if you have a template named layout.html:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<title>{{ title }} | Site.com</title>
....
{% block content %}{% endblock content %}
....
</html>
And you have this child template, index.html:
{% extends "layout.html" %}
{% set title = 'Homepage' %}
{% block content %}
(html code here)
{% endblock content %}
Then the reference to title in the parent would resolve to 'Homepage'. You can do this with any type of variable. For what you're doing, I don't think there is any need for macros - if you take advantage of this feature and place blocks well, you will be able to do pretty much everything you need to do as far as layouts are concerned. I would look at some of the templates used by Plurk Solace, which is written by one of the Jinja2 authors, if you want to get a good idea of when to use various features of Jinja2.

Categories