I'm making a Flask app for local development (on a Mac) of HTML templates that will eventually be served through ASP.NET.
For the purposes of local development, I want a way to replace the contents of .NET-style tokens with some data, meaning that Jinja2 would need to be able to recognize %% ... %% tokens in addition to the standard ones: {{ ... }}, <% ... %>, etc.
Everything I've found online pertains to the inclusion of some new functionality within the existing tags (e.g. {{ my_custom_function | arg1 arg2 }})
But what about defining a new pattern for tags altogether? Has anyone done this successfully? And will it require modification to the Jinja2 core?
As far as I know, you can use one set for block_start_string and block_end_string, as well as one set for variable_start_string and variable_end_string.
From jinja2/environment.py
`block_start_string`
The string marking the begin of a block. Defaults to ``'{%'``.
`block_end_string`
The string marking the end of a block. Defaults to ``'%}'``.
`variable_start_string`
The string marking the begin of a print statement.
Defaults to ``'{{'``.
`variable_end_string`
The string marking the end of a print statement. Defaults to
``'}}'``.
You can override these with environment variables. Though, I don't think there is a way to have multiple types recognized. For instance, you can't have {{ and <% both work, but with a little hackery you certainly could.
In addition to the correct answer maybe an example implementation to change the variable marker:
Add following header to the first line in the jinja2 template file.
#jinja2: variable_start_string: "#{" , variable_end_string: "}#"
Related
I want to create a pdf with latex through a django view. I created the view and set up a latex template and it works fine except for one thing. When I place a variable in the latex template I have to use spaces around the curly brackets like so:
\somevalue{ {{variable}} }
Otherwise django won't check that it is as a variable. The latex syntax checker already tells me "Unintendes whitespace around }?!". I can pass the variable into the template through my view and the pdf get created but then I have whitespaces around the inserted text.
Does anybody has an idea how to solve this?
Based on some google research, I'd recommend switching templating engines to Jinja, which is supported by Django and has configurable syntax.
Be warned, I haven't fully tested this.
Here's how your latex templates would look like:
\somevalue{((variable))}
The most important part is setting the variable_start_string and variable_end_string options:
>>> import jinja2
>>> env = jinja2.Environment(variable_start_string='((', variable_end_string='))')
>>> t = env.from_string("\somevalue{((variable))}")
>>> t.render(name='Attention!')
'\\somevalue{Attention!}'
Jinja's switching documentation outlines the (few) syntax differences. From the FAQ:
The default syntax of Jinja2 matches Django syntax in many ways. However this similarity doesn’t mean that you can use a Django template unmodified in Jinja2. For example filter arguments use a function call syntax rather than a colon to separate filter name and arguments. Additionally the extension interface in Jinja is fundamentally different from the Django one which means that your custom tags won’t work any longer.
Django 1.9 and 1.8 and maybe other versions have built-in support for Jinja.
I haven't found an example of configuring Jinja syntax in Django, and I can't test this at the moment but I believe you need to change the
TEMPLATES['OPTIONS'] dictionary as needed:
block_start_string='(#',
block_end_string='#)',
variable_start_string='((',
variable_end_string='))',
comment_start_string='((#',
comment_end_string='#))',
The solution I found is to remove extra spaces after template rendering:
template = select_template(self.get_template_names())
latex_content = template.render(context)
latex_content = re.sub(r'\{ ', '{', latex_content)
latex_content = re.sub(r' \}', '}', latex_content)
This has the benefit of not requiring extra template tags flooding the template. However this as the drawback to require template writers to be aware of this behavior and take it into account wherever curly braces are used.
In practice, it would probably be better to write a custom Template class that handles this.
Edit
Note that using this method, there is no reason to use a space as separator rather than any other character. In order to make it clearer for template writers/readers, you can use another character that would ring him a bell. For instance:
template.tex
\somevalue{§{{ variable }}§}
views.py
template = select_template(self.get_template_names())
latex_content = template.render(context)
latex_content = re.sub(r'\{§', '{', latex_content)
latex_content = re.sub(r'§\}', '}', latex_content)
Which makes it clear that if there is a space, it is intended. And I think it's already clear that § everywhere is not intended to be displayed.
I am trying to use jinja2 as follows.
Suppose,
Following are tags:
tags: {"world":"WORLD", "c language": "Dennis Ritchie", "apple":"JOBS" }
Input:
HELLO {{ world }}, C is written by **{{ c language }}**, **}}** while **{{** java is written by {{ java }}, hola.
Output:
HELLO WORLD, C is written by Dennis Ritchie, **}}** while **{{** java is written by, hola.
So in short there are following things I have to do.
delimiters - {{ & }}
If there is no tag predefined, it should put empty.
If there is only single delimiter {{ or }} ( I mean not pair) ,it should not consider tag else it should be printed as it it.
Tags should allow spaces.
Out of 4, for only 1 & 2 jinja2 is working fine.
from jinja2 import Template
t = Template(input_string)
t.render(context)
But for 3rd & 4th, it's not working.(or I am mistaking.)
I found only 1 template engine called "mustache" which supports above all 4 conditions. But I don't know how it works in case of performance.
As jinja2 is mature template engine, I think it's possible to customize default behaviour.
Can anybody know solution?
Thnx in advance.
My primary testing shows that Mustache(Pystache) is too faster than jinja2. If possible please give expert opinion.
http://mustache.github.io/
https://github.com/defunkt/pystache
Finally I continue with mustache. It's really awesome template engine.
http://mustache.github.io/
For mustache build for python
https://github.com/defunkt/pystache
I don't think this is possible. The documentation is quite clear on identifiers:
Jinja2 uses the regular Python 2.x naming rules. Valid identifiers
have to match [a-zA-Z_][a-zA-Z0-9_]*. As a matter of fact non ASCII
characters are currently not allowed. This limitation will probably go
away as soon as unicode identifiers are fully specified for Python 3.
I'm pretty new to both Python and Flask (with Jinja2 as template engine) and I am not sure I am doing it the right way. I am using Flask-Babel extension to add i18n support to my web application. I want to get localized strings from my js code, for instance:
var helloWorld = gettext('Hello, world');
console.log(helloWorld); //should log a localized hello world message
For this, I configured babel (babel.cfg):
[python: **/**.py]
[jinja2: **/**.html]
extensions=jinja2.ext.autoescape,jinja2.ext.with_
[javascript: **/**.js]
encoding = utf-8
And its initialization is (imports omitted for simplicity):
#main Flask app
app = Flask(__name__)
#localization
babel = Babel(app)
LANGUAGES = {
'ca': 'Catalan',
'en': 'English',
'de': 'Deutsch',
'es': 'Español',
'fi': 'Finnish',
'it': 'Italian'
}
#babel.localeselector
def get_locale():
return request.accept_languages.best_match(LANGUAGES.keys())
#some more stuff...
Babel identifies that string when building the POT/PO language files, but it seems I can't access these localized strings from js code since gettext function is not defined. It seems like Jinja2 is ignoring this part.
Any hints?
I have finally found a solution, although I am not sure it is the way to go. The idea is to wrap the javascript code within an html template, which is interpretated by Jinja2 before it is rendered and apply a custom Jinja2 filter to get rid of some minor issues. I tried to keep js files separately but it did not work.
It seems that gettext function can be used like so:
var helloWorld = {{gettext('Hello, world')}};
But then, no quotes are inserted, and hence, js interpreter throws an error:
var helloWorld = Hello, world;
That's why I have finally applied a custom filter. A working example would be as follows.
hello_world.html:
<script type="text/javascript">
var x = {{gettext('Hello, world')|generate_string|safe}};
console.log(x); //logs the localized hello world message
</script>
app.py:
#Jinja2 filters
from jinja2 import evalcontextfilter, Markup
#Mind the hack! Babel does not work well within js code
#app.template_filter()
#evalcontextfilter
def generate_string(eval_ctx, localized_value):
if localized_value is None:
return ""
else:
return Markup("\"" + localized_value + "\"").unescape()
Hope this helps!
Providing translations in rendered JavaScript is a bit fragile. Also, I usually do not generate JavaScript using Jinja because it uses the same type of brackets and easily turns into mess when abused (it's always possible to have dynamic data and static JavaScript).
Alternative lightweight approach is to do the same JSON trick, but using data-attributes:
<div id="view-i18n" data-i18n='{{ view_i18n|tojson }}'> ... </div>
(NB: single quotes!)
But it's also good for a limited number of translations.
Perhaps, the most solid approach is to have the same translations in JavaScript as there are in the Flask app.
With a help of a utility called pojson it is possible to convert po-files to json (see https://github.com/ZeWaren/flask-i18n-example for an example) as part of the build process (for instance, right after making mo-files). Translations can be easily added to some unique enough global namespace variable by prepending the output of pojson with var some_unique_name = to have access to it. Or put the file under static into locale-specific file (eg static/view-es.json , static/view-fr.json, etc) and get it with ajax call.
Some things to consider though. You may need to break your translation domain into separate Python and Javascript by controlling babel extraction options if you really want to make JSON smaller. Also, having all translation strings in Javascript has security aspects. Maybe, you do not want to expose certain phrases only admins see to be open to other category of users. But then more translation domains are needed for different levels of access. Also header information may need to be removed to prevent leaking translator's emails and whatnot. This, of course, complicates the build process somewhat, but the more translation JavaScript side needs with time, the more automation pays itself off.
I am pretty new to django, but have many years experience coding in the java world, so I feel ridiculous asking this question - I am sure the answer is obvious and I am just missing it. I can't seem to find the right way to query this in google or something... I have searched through the django docs and it either isn't there or I am just not seeing it. All I want to do is in a template test if the var is not null OR an empty string OR just a bunch of spaces. I have an issue where spaces are getting introduced into my field - another issue I have to, and will, work out... but, I want my logic to work regardless. Right now, because my string contains just spaces simply doing this: {% if lesson.assignment %} always passes even though I don't want it to. I have looked for a trim type functionality that would work between {% %}, but I can't seem to find anything. I have tried strip, but it doesn't work between {% %}. Can someone please point me in the direction of the answer... some docs I might have missed... something?
Thanks a ton in advance!
{% if lesson.assignment and lesson.assignment.strip %}
The .strip calls str.strip() so you can handle whitespace-only strings as empty, while the preceding check makes sure we weed out None first (which would not have the .strip() method)
Proof that it works (in ./manage.py shell):
>>> import django
>>> from django.template import Template, Context
>>> t = Template("{% if x and x.strip %}OK{% else %}Empty{% endif %}")
>>> t.render(Context({"x": "ola"}))
u'OK'
>>> t.render(Context({"x": " "}))
u'Empty'
>>> t.render(Context({"x": ""}))
u'Empty'
>>> t.render(Context({"x": None}))
u'Empty'
This may not have been available when the question was posted but one option could be using the built in template filter default_if_none (Django Documentation Link).
For example:
{{ lesson.assignment|default_if_none:"Empty" }}
If lesson.assignment is a model field, you could define a helper function in your model and use it:
class Lesson(models.Model):
assignment = models.CharField(..., null=True, ...)
# ...
#property
def has_assignment(self):
return self.assignment is not None and self.assignment.strip() != ""
Then use {% if lesson.has_assignment %} in your template.
You can simulate it by using the cut filter to remove all spaces. But you should probably find out why it contains all spaces in the first place.
You can call any built-in methods anywhere in a Django template variable. For example, you can call the Python string method strip. So this will work:
{% if lesson.assignment.strip %}
Here is a simple way to do this, from Django version 3.2
{{ lesson.assignment|default:"nothing" }}
This works for both cases (empty string and None object).
ref link: https://docs.djangoproject.com/en/3.2/ref/templates/builtins/#std:templatefilter-default
Situation:
I am writing a basic templating system in Python/mod_python that reads in a main HTML template and replaces instances of ":value:" throughout the document with additional HTML or db results and then returns it as a view to the user.
I am not trying to replace all instances of 1 substring. Values can vary. There is a finite list of what's acceptable. It is not unlimited. The syntax for the values is [colon]value[colon]. Examples might be ":gallery: , :related: , :comments:". The replacement may be additional static HTML or a call to a function. The functions may vary as well.
Question:
What's the most efficient way to read in the main HTML file and replace the unknown combination of values with their appropriate replacement?
Thanks in advance for any thoughts/solutions,
c
There are dozens of templating options that already exist. Consider genshi, mako, jinja2, django templates, or more.
You'll find that you're reinventing the wheel with little/no benefit.
If you can't use an existing templating system for whatever reason, your problem seems best tackled with regular expressions:
import re
valre = re.compile(r':\w+:')
def dosub(correspvals, correspfuns, lastditch):
def f(value):
v = value.group()[1:-1]
if v in correspvals:
return correspvals[v]
if v in correspfuns:
return correspfuns[v]() # or whatever args you need
# what if a value has neither a corresponding value to
# substitute, NOR a function to call? Whatever...:
return lastditch(v)
return f
replacer = dosub(adict, another, somefun)
thehtml = valre.sub(replacer, thehtml)
Basically you'll need two dictionaries (one mapping values to corresponding values, another mapping values to corresponding functions to be called) and a function to be called as a last-ditch attempt for values that can't be found in either dictionary; the code above shows you how to put these things together (I'm using a closure, a class would of course do just as well) and how to apply them for the required replacement task.
This is probably a job for a templating engine and for Python there are a number of choices. In this stackoveflow question people have listed their favourites and some helpfully explain why: What is your single favorite Python templating engine?