All my JavaScript is run through Django's compiler, allowing me to inject HTML strings Underscore templates in the following manner:
var htmlStr = '{% filter convert_js_template %}{% include "my_file.html" %}{% endfilter %}'
This code runs the output from the included HTML file through the convert_js_template filter, which simply removes line breaks and escapes single quotation marks so that the final JS string is valid. To make it more readable, however, I'd like to be able to simply write something like the following:
var htmlStr = '{% convert_js_template "my_file.html" %}'
How can I create a convert_js_template function that will accomplish this? My sense is that it needs to start out by doing the following:
Grab the contents of the desired file
Parse the contents for any Django template tags
I tried the following:
#register.filter('convert_js_template')
def convert_js_template(path):
value = include(path)
return value.replace('\n', '').replace('\r', '').replace("'", "\\'")
I initially received the error NameError: global name 'include' is not defined, so then I added from django.conf.urls import include to the file and am now receiving a different error: ImportError: Import by filename is not supported.
And this is where I'm stuck :)
My solution doesn't involve a custom "include" function exactly. Instead, it manually loads and renders the desired template file:
from django.template import Library, loader, Context
...
register = Library()
...
#register.filter('js_template')
def js_template(template_path):
tpl = loader.get_template(template_path)
return tpl.render(Context({})).replace('\n', '').replace('\r', '').replace("'", "\\'")
js_template.is_safe = True
Note that template_path should be relative to the directory you have set for Django to look in for templates.
Usage:
var htmlString = '{{ "path/to/my_template.html" | js_template }}';
Related
I have a one template say:
default_form.html
which receives in its context a variable, say variant.
I can of course render the contents of the variable with {{ variant }}
And here:
https://stackoverflow.com/a/28076380/4002633
I see a wonderful way to load a template if it exists. It works nicely if I have a template called variant as follows:
{% try_include variant %}
But I am hoping to go one step further. I would like to use a constructed template name, in the example above and include if it exists a template named default_form_variant.html.
I imagine something like this:
{% try_include "default_form_{{variant}}.html" %}
but even more general rather than encoding the current template's name, getting that, if possible, from the context itself or in a custom tag from the parser.
I am struggling to see how and where in the custom tag definition I'd have access to three things at once:
The name of the current template: default_form.html in this case.
The argument to the tag: variant in this case
The ability to construct a template name from the first two (easy enough with os.path) and inject it into the do_include handler.
Well, as no-one was rushing to answer this I experimented with the wonderful solution to another problem posted here:
https://stackoverflow.com/a/28076380/4002633
at some length and have implemented the following with success:
class IncludeVariant(template.Node):
'''
A Template Node that tries to include a template file named as a variant
of the file it's included in. That is if it's in a template named:
form_data.html
as:
{% include_variant context_var %}
it will try and include:
form_data_context_var.html
where context_var is a context variable.
For help on custom template tags:
https://docs.djangoproject.com/en/3.1/howto/custom-template-tags/#writing-the-compilation-function
'''
def __init__(self, parser, token):
self.parser = parser
self.token = token
def render(self, context):
try:
words = self.token.split_contents()
variant = context.get(self.token.contents.split()[1], self.token.contents.split()[1])
path = context.template_name
parts = os.path.splitext(path)
words[1] = f"'{parts[0]}_{variant}{parts[1]}'"
self.token.contents = " ".join(words)
include = do_include(self.parser, self.token)
return include.render(context)
except template.TemplateDoesNotExist:
return ''
#register.tag('include_variant')
def include_variant(parser, token):
'''
Include the specified variant on this template but only if it exists.
:param parser:
:param token:
'''
return IncludeVariant(parser, token)
It is a mildly risky approach in that it rests on premises that stood up to empirical testing but I can't find clear documentation for. Namely that we can rebuild the passed-in token as I have done (a re-join of a token.split_contents()) and that this is robust. It certainly seems to be and inspecting do_include() I am satisfied it's functional.
This permits me to include variants of the current template based on the contents of a context variable.
I have a variable
text = "replace some word"
in a view. I want to replace 'some' to bold. Like this:
"replace **some** word"
How to handle that variable in django template?
Obviously, the ** do not make a bold string in a template and you will have a much harder time trying to replace these markers with appropriate opening and closing tags in the template, e.g via a custom filter. However, you can make it bold in the view by applying the necessary HTML there and marking it safe:
# views.py
from django.utils.safestring import mark_safe
# in your view
# ...
text = "replace some word"
# add the appropriate html tags
text = text.replace('some', '<strong>some</strong>')
# now you have to mark it as safe so the tags will be rendered as tags
text = mark_safe(text)
# ...
return render(reqest, template, {.., 'text': text, ...})
Now you can just use it like a normal variable in the template via {{ text }}
You can create custom template tags to render these type of things. You have to write some extra lines of code. But once you do, it's reusable and less tedious to use replace function every time.
Create a custom tag/filter file called mytags.py, your app layout might look like this:
myapp/
__init__.py
models.py
templatetags/
__init__.py
mytags.py
views.py
Write in mytags.py:
from django import template
register = template.Library()
def bold(text):
return text.replace('**','<strong>',1).replace('**','</strong>',1)
register.filter('bold', bold)
In your template, first load the custom tag file:
{% load mytags %}
Apply this custom tag to the text:
{{ text|bold }}
For reference: https://docs.djangoproject.com/en/2.1/howto/custom-template-tags/
I have following jinja template in variable code:
code = """{% set p1 = 'hello world' %}
<p>{{p1}}</p>"""
I am getting parsed template using parse method,
from jinja2 import Environment
env = Environment()
template = env.parse(code)
now, how to get the original code back from the parsed template?
I have a route method:
#app.route('/add', methods=['GET'])
def add_view():
return render_template('add.html', categories=api.categories())
Then I tried to display categories as JSON inside template add.html:
{{ categories | json }}
It does not work
I find it hard to understand exactly what you're looking for here so I'd like to see more details but here's an answer based on what I think you're asking for (I'll edit this to suit your needs / remove this comment entirely if things change).
You're invoking api.categories() and are looking to render this as JSON on your HTML template, yes?
OK what I'd recommend here is to ensure that api.categories() is returning an instance of dict. For example, your api.categories() call should return something like this:
{
"testKey1": "testValue1",
"testKey2": "testValue2"
}
Now to render this as JSON in your HTML template. You can import the json module in your Flask module using the following import:
import json
Now your return statement in your add_view method will be as follows:
return render_template('add.html', categories=json.dumps(api.categories()))
You can now do something like as follows in your HTML template:
<script>
document.write("{{categories}}");
<script>
This should render your JSON for you just fine.
I try to enable the trans tag and I've made a test template i18n.html:
{% trans %}For sale{% endtrans %}--{{message}}--{{decimal_format}}
Here is my python code according to the manpages:
from webapp2_extras import i18n as multilingua
import jinja2
from webapp2_extras.i18n import lazy_gettext as gettext
from webapp2_extras.i18n import lazy_gettext as _
from jinja2 import Environment, FileSystemLoader
jinja_environment = jinja2.Environment(loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
extensions=['jinja2.ext.i18n'])
# The code below seems wrong since it is django but it was the only way I could make the page load
jinja_environment.install_gettext_translations(django.utils.translation)
class HelloWorldHandler(webapp2.RequestHandler):
def get(self):
# Set the requested locale.
locale = self.request.GET.get('locale', 'pt')
multilingua.get_i18n().set_locale(locale)
message = multilingua.gettext('For sale')
#django.utils.translation.activate('pt')
template = jinja_environment.get_template('templates/i18n.html')
decimal_format = multilingua.I18n(self.request).format_decimal(1000)
self.response.out.write(template.render(message=message, decimal_format=decimal_format))
I could not make it work without django and therefore I ask how to lose the django translation and staying with webapp2.i18n + jinja instead.
There was also a discussion in a thread where I'm not the only one saying that documentation is somewhat incomplete or hard to find. Could you please answer or comment which is the recommended way of making the trans tag work and why I must include jinja_environment.install_gettext_translations(django.utils.translation)
?
When I try to remove my use of django I also lose the functions of webapp2.i18n. My locale files are both in locale/... and conf/locale.. since the first is the default for webapp2 and the second is the default for django translations, so I could really use some guidelines for best practice here to get rid of the django dependecies and use webapp2 and jinja for rendering my localizations.
If to any help, I did receive an error message when trying to remove django:
self.response.out.write(template.render(message=message, decimal_format=decimal_format))
File "/media/Lexar/montao/montaoproject/jinja2/environment.py", line 894, in render
return self.environment.handle_exception(exc_info, True)
File "/media/Lexar/montao/montaoproject/templates/i18n.html", line 2, in top-level template code
{{ _('For sale') }}--{{message}}--{{decimal_format}}
UndefinedError: 'gettext' is undefined
Thank you
Take a look at Jinja2's i18n Extension documentation. Calling install_gettext_translations basically sets the object through which Jinja2 will call gettext, ngettext, etc, in order to translate strings when it encounters a {% trans %} tag.
Since those functions are defined on webapp2.i18n (see here), jinja2 will successfully call those functions to retrieve translations, dependent upon your call to set_locale inside of the request. I don't have the code in front of me, but I'd guess that gettext and company defined in webapp2.i18n are merely proxies to call webapp.i18.get_i18n().gettext, which is the magic that makes all of this work.
Here is a working example for Django+jinja2:
from jinja2 import PackageLoader, Environment
from django.utils import translation
...
jinja_environment = Environment(loader=PackageLoader('website', 'templates'),
extensions=['jinja2.ext.i18n'])
jinja_environment.install_gettext_translations(translation)
template = jinja_environment.get_template('test.jinja.html')
page_next_app_table = template.render()
...
In test.jinja.html:
<html>
<b> {{ _( "Traslate This" ) }}:</b>
</html>