Is there a way to access javascript variables within jinja2 template braces? - flask

I have a flask route
#app.route('/another/<int:value>')
def another():
return 'another'
Using javascript, I'm trying to access the url for this route using url_for within jinja2 template braces
<script>
const v = 1;
const sample = "{{url_for('another', value=v)}}";
</script>
doing this raises an error jinja2.exceptions.UndefinedError: 'v' is undefined.
I suspect v is outside the scope of the jinja templates.
Is there a way around this?

Related

My custom Jinja template filter will not register or work?

I've created a custom filter but my Flask server is getting an internal error.
#app.template_filter('doSomething')
def doSomething(input):
print(input)
return input
HTML
<p>{{ doSomething('Test') }}</p>
Error
jinja2.exceptions.UndefinedError: 'doSomething' is undefined
After some searching, trial and error, this is the only one that seems to work for me:
def doSomething(input):
print(input)
return input
app.jinja_env.globals.update(doSomething=doSomething)
In the documentation it says :doSomethingWith({{ user.username|tojson|safe }}); which means that the user.username object will be json serialised and then passed to the doSomethingWith which is a JavaScript function;not like {{doSomethingWith(user.username|tojson|safe);}} you used in your example which looks like you are trying to explicitly call a python function from the template, so try this and if you still have any troubles tell us more about what exactly you're trying to achieve.
Just to make this answer a bit useful while also addressing the question, I'll show a custom filter function for which I had a need. You can use the .app_template_filter() decorator method on your Flask app to effectively "register" the new template filter function. Once it's registered you can use it on any template within the context of your app or blueprint.
If you're using blueprints, in your controller you'd do something like this:
#some_blueprint.app_template_filter()
def utcToStrFilter(someUTCFloat):
try:
shiftedDateStr = datetime.strftime(datetime.fromtimestamp(someUTCFloat), "%Y-%m-%d %H:%M:%S")
return shiftedDateStr
except:
return ""
If you're not using blueprints then your decorator would be:
#app.app_template_filter()
or
#current_app.app_template_filter()
In any case, then in your template you just use it like a regular jinja filter:
{{ something.fileSavetimeUTC | utcToStrFilter }}
The key thing is to make the app aware of the function, which is what .app_template_filter() is for.

django dynamic javascript in custom widget

I have a javascript code in rendered html page of custom widget. I want to move the js code to separate js file.
However, it must be dynamic not static media file.
I wrote a custom widget:
class CustomWidget(CustomWidgetBase):
css = {
'all': (
config['custom_css'] +
config['extra_css']
)
}
js = (
config['custom_js'] +
config['extra_js']
)
#property
def media(self):
media = super(CustomWidget, self).media
media.add_css(CustomWidget.css)
media.add_js(CustomWidget.js)
return media
def render(self, name, value, attrs=None):
attrs_for_textarea = attrs.copy()
attrs_for_textarea['hidden'] = 'true'
attrs_for_textarea['id'] += '-textarea'
html = super(CustomWidget, self).render(name, value, attrs_for_textarea)
html += render_to_string(
'app/custom_widget.html',
{
'id': attrs['id'].replace('-', '_'),
'id_src': attrs['id'],
'value': value if value else '',
'settings': json.dumps(self.template_contexts()),
'STATIC_URL': settings.STATIC_URL,
'CSRF_COOKIE_NAME': settings.CSRF_COOKIE_NAME,
}
)
CustomWidget.js += (os.path.join(settings.STATIC_URL, 'app/my.js'),)
return mark_safe(html)
'app/custom_widget.html' has javascript code:
{% load staticfiles %}
<div id='{{ id_src }}'>{{ value|safe }}</div>
<script>
$(function() {
var {{ id }}_textarea = window.document.getElementById('{{ id_src }}-textarea');
... omitted ...
I'd like to move javascript code in 'app/custom_widget.html' into 'app/my.js' because it makes widget code dependent on the order of javascript declarations.
Thus, 'app/my.js' must be dynamically generated with the values passed by Django view. I want to place 'app/my.js' at the bottom of the page.
Thank you.
Two options:
You can put a list of variable declarations in a script tag in your template, making sure they're declared before you import your static javascript. Then your static javascript refers to variable values that it knows were appropriately set by the server.
Put your dynamic values in data attributes in your HTML elements. Then write your static javascript to look up the appropriate server-set data based on what it's manipulating.
What you’re doing in your sample code looks very complex and I’m not sure it needs to be. If I interpret correctly, you are trying to make sure that when you insert a custom widget, a custom javascript behaviour will be applied to it.
I think the second option is more appropriate for that behaviour and that you should probably use jquery to make it as simple to implement as possible.
Firstly, in your custom widget’s html template, give your custom widget a class that lets your javascript know to apply custom behaviours to it.
eg,
<input class=“custom-behaviour-widget” data-id_name=“{{ id }}" data-other_variable_name=“other_variable-value" …>
Then write static javascript that looks like:
<script>
$( document ).ready(function () {
$(‘.custom-behaviour-widget’).each(
value_that_i_want = $(this).data(‘id_name’);
...
);
});
</script>

flask render_template not working with anchor tag # - jinja2.exceptions.TemplateNotFound

In the url_for method, you can pass in an _anchor parameter to load up an anchor tag on page load.
How would i do the same thing with render_template ?
I can
render_template('profile/index.html')
without any issues but when i add the anchor tag at the end of the html and try to do
render_template('profile/index.html#h1")
I get the template not found error
jinja2.exceptions.TemplateNotFound
TemplateNotFound: profile/index.html#h1
Essentially thats what I ended up doing, I passed in a value along with the render_template
return render_template('profile/index.html', set_tab=1)
On the client side in javascript, i did the following to set the tab
<script>
var obj_load={% if set_tab==1%}1{%else%}0{%endif%};
tab_load=(obj_load?"#h1":"#");
$(document).ready(function () {
if(location.hash || obj_load) {
$('a[href=' + (location.hash || tab_load) + ']').tab('show');
}
});
</script>
fyi, im using bootstrap 3's tab-pane

Getting value of URL-encoded request.GET variable in Django Template

I am using Django 1.3 with Python 2.7.2 on Windows 7 x64.
I have a URL pattern like this:
url(r'^(?P<club_id>\d+)/$', 'view_club')
From the pattern I want to be able to use request.GET in my templates to be able to create URLs to my views based on this GET variable (club_id). I have read online that I would be able to do:
{{ request.GET.club_id }}
However, this is always showing up as blank. I cannot figure out the reason why. I have the request context processor in my list of default context processors.
Thanks for the help.
In your example, club_id is not a GET parameter, it is part of the path in the URL. Your view function takes club_id as an argument, it should then put it in the context so the template can access it.
For example, your view function:
def view_club(request, club_id):
return render(request, "template_name.html", { 'club_id': club_id })
then in your template:
{{ club_id }}
IF you are passing an variable in the URL - in this case club_id - simply include it as an argument in your view function next to request:
def get_club(request,club_id):
club = Club.object.get(pk=club_id)
return render_to_response('club.html', {'club': club})
now in your template you can access {{club}} as the individually selected object and do things like {{club.name}} This also works for variables with multiple objects which you would use a for loop to iterate through.

How can you use Django template tags in a Jinja2 template?

There are a number of off-the-shelf Django packages that offer template tags, and I'd like to use them in my project despite the fact that it uses Jinja2.
I've seen some workarounds that allow other template engines to use Django tags. These involve creating a mini-template in a string, and passing it to the Django template processor along with the current context. Here are two example of what I'm talking about: Mako templates using Django template tags and Jinja2 templates using Django template tags.
I'm wondering if there's a less hackish solution.
what about moving problem to python's scope
and importing one python's function into another, this way:
in your_jinja_templatetags.py
from some_django_templatetags import somefilter as base_somefilter
#library.filter
def somefilter(value):
return base_somefilter(value)
Unfortunately Django template tags are not directly translatable to Jinja.
Django Template tags can do many different things, and are usually implemented as functions. Fortunately, Jinja can register these functions as globals and call them. This works for "simple" template tags out of the box.
If you are using the official Django Jinja2 backend, see https://docs.djangoproject.com/en/4.1/topics/templates/#django.template.backends.jinja2.Jinja2 to find out how to register a function (in this case your template tag) as a global. Once this is done, you can call the function from within your template as {{ your_tag_function() }}. Remember to pass whatever arguments the template tag function needs!
I like to use the https://github.com/niwinz/django-jinja backend, myself- if you are using this backend, see https://niwi.nz/django-jinja/latest/#_custom_filters_globals_constants_and_tests for how to register a function as a global.
Unfortunately, this only works for "simple" tags out of the box. Some tags are "inclusion tags" or otherwise depend on the Django Template Engine. Since Django Templates can't be rendered within the Jinja engine, you have to do a bit more work to translate these types of tags. You will need to look at the source code to tell the difference and see what they are doing.
What I usually do for the more complicated tags is create a "jinja_functions.py" script in my Django app, then in that script I import the original template tag. Then I write a function that wraps it- calling the original function, getting back usually a dictionary, and then using that dict to render a string which is returned... most tags are simple enough I just use an f string or .format to format them with the results of the dict.
Then I register my new wrapper function as a jinja global and use it in my jinja templates!
The following is an example of where I re-implemented the template tags provided by https://github.com/timonweb/django-tailwind for use with jinja. I combined both the tailwind_css and tailwinf_preload_css tags into a single function, tailwind_css, which I register with Jinja as a global as per the instructions above, then I can call that function as {{ tailwind_css(prefetch=True) }} or {{ tailwind_css() }} within my templates.
from django.templatetags.static import static
from tailwind import get_config
from tailwind.utils import is_path_absolute
def tailwind_css(v=None, prefetch=False):
tailwind_css_path = get_config("TAILWIND_CSS_PATH")
if not is_path_absolute(tailwind_css_path):
tailwind_css_path = static(tailwind_css_path)
if v:
tailwind_css_path = f"{tailwind_css_path}?v={v}"
if prefetch:
rel = 'rel="preload"'
as_str = ' as="style"'
else:
rel = 'rel="stylesheet"'
as_str = ""
return f'<link {rel} href="{tailwind_css_path}"{as_str}>'
In conclusion it works well out of the box for "simple" tags because you can just register them as global functions in Jinja... but for "include" tags or other tags that rely on the Django Template engine, you will have to write your own function :( But usually its not too much work!