WTForms use quotes in Jinja2 - flask

I've got the following WTForms class:
from flask_wtf import FlaskForm
from wtforms import SelectField
class MonitorLevel(FlaskForm):
monitor = SelectField('Monitor', choices=MONITOR_CHOICES)
Which can be rendered using the following jinja2-code:
{{ form.monitor() }}
However, I want to execute a JS-script when the value changes, so I've added the following:
{{ form.monitor(**{'onchange': 'sendForm();'}) }}
Which works fine, but now I want to pass a variable (which is a string), as an argument:
{{ form.monitor(**{'onchange': 'sendForm("{}");'.format(variable)}) }}
However, this renders as:
<select id="monitor" name="monitor" onchange="sendForm("name");">...</select>
So, I tried to escape this using the safe function, but this doesn't work. I've also tried to escape the quote by: \", but that doesn't work as well.
Any ideas of adding a quote in the value of the dict?
Thanks in advance,

From the WTForms documentation https://wtforms.readthedocs.io/en/2.3.x/widgets/#widget-building-utilities :
WTForms uses MarkupSafe to escape unsafe HTML characters before rendering. You can mark a string using markupsafe.Markup to indicate that it should not be escaped.
Without using markupsafe.Markup I had the same error:
{{ input_field(**{'#click': '"show=true"'})|safe }}
gives
<input #click=""show=true"">
instead of
<input #click="'show=true'">
Using the markupsafe module inside the Jinja2 template:
{{ input_field(**{'#click': markupsafe.Markup("show=true")})|safe }}
does the job:
<input #click="show=true">
Be careful: WTForm encloses the string in the double quotes ", so you need to take a usual care about the " inside your string.
BAD WAY
{{ input_field(**{'#click': markupsafe.Markup('console.log("show=true")')})|safe }}
will result in
<input #click="console.log("show=true")">
which is wrong (one string is not inside the other).
GOOD WAY
{{ input_field(**{'#click': markupsafe.Markup("console.log('show=true')")})|safe }}
will give
<input #click="console.log('show=true')"

This behavior is normal, WTForms use escape(s, quote=True) to render HTML attributes values (escape documentation)
You can look function def html_params(**kwargs): on Github directly fore more informations.
Basically you don't have to change your code because :
Javascript still works like a charm, your browser transforms HTML entities on the fly (sendForm() is run onchange).
onchange="sendForm("name");" is NOT valid HTML attribute if you print it without escape(s, quote=True).

Related

setting a dynamic data attribute on flask wtforms jinja select

Have a flask wtforms select field and trying to incorporate htmx ajax call, which has dashes in data attributes so I found a solution on SO like the following:
{{ form.dia(class="form-select", **{'hx-post': "/pulleys/{{pulley_id}}/hub_type", 'hx-target': "#hub-shaft-selection", 'hx-swap': "innerHTML"}) }}
but the {{pulley_id}} doesn't get parsed by the template, I guess it's already inside another {{ }}.
Is there a way to build the dynamic part as shown above so it ends up as
'hx-post'="/pulleys/1/hub_type"
when fully rendered for pulley_id=1
If pulley_id is a variable either inside a loop or passed into render_template your should be able to format the string:
{{ form.dia(class_="form-select", **{'hx-post': "/pulleys/%s/hub_type"|format(pulley_id), 'hx-target': "#hub-shaft-selection", 'hx-swap': "innerHTML"}) }}
Note: you also want class_ not class if you're trying to set the HTML class attribute.

Trying to render a django form field as a variable in a script tag in an html template, but the javascript isn't working

My issue is simple enough--I am trying to render a form field from a django form into a javascript variable, defined within a <script> tag, within a django template.
When I output a CharField, there's no problem. But when I try to render a ChoiceField, the resulting output breaks the html, and prevents the script tag from correctly parsing my variable.
To demonstrate my setup, I have a form defined in forms.py, exactly like this example form:
from django import forms
form = TestForm(forms.Form):
testfield = forms.ChoiceField(initial="increase_rate",
choices=[
("a", "option a"),
("b", "option b"),
("c", "option c"),
("d", "option d")
])
I am instantiating the form in views.py, and passing it into a django template to be rendered.
from django.shortcuts import render
from .forms import TestForm
[...]
#require_http_methods(["GET"])
def webpage(request):
form = TestForm()
return render(request, 'index.html', {"form":form})
Then, finally, in my template, I have something like the following:
[...]
<script>
window.testfield = '{{ form.testfield }}'
</script>
[...]
Up until this point, everything works perfectly. No trouble at all. But when I render the field into the template, and inspect it in my browser, I get the following:
<script>
window.testfield = '<select name="trigger" id="id_trigger">
<option value="a" selected>option a</option>
<option value="b">option b</option>
<option value="c">option c</option>
<option value="d">option d</option>
</select>'
</script>
This output breaks the html, and prevents the script tag from being interpreted as a variable like I want. This is a major problem, because I want to reuse these programmatically elsewhere on the page.
I tried the following:
<script>
window.testfield = '{{ form.testfield|escape }}'
</script>
But was still unsuccessful. Any help anyone can give would be greatly appreciated!
I am actively researching a solution. My current guess is that the output needs to be escaped somehow that I don't understand. I figure the template tags and filters have my answer, I just have to find it. Will post an update once a solution is found.
Use <script type="text/template"></script>.
This way the browser knows it's just text and will ignore it.
So, I figured it out. Turns out that the issue was that the presence of white space, line breaks, and unescaped double quotes (") were breaking the tag when it was parsed at HTML.
So I ended up using the following:
{% spaceless %}
<script>
window.testfield = '{{ form.testfield|addslashes }}'
</script>
{% endspaceless %}
And it worked, allowing me to store the string representation of the django form field in a javascript variable. As per the documentation, the {% spaceless %} "Removes whitespace between HTML tags. This includes tab characters and newlines." [1]. As for the filter |addslashes, it "Adds slashes before quotes. Useful for escaping strings in CSV" [2]. In my case, both solutions were needed, as without either of them, the script tag broke.
As for why the |escape filter didn't work on it's own, I'm not sure. Reading the documentation, it seems like it probably should have. The following is what the |escape filter actually does [3]:
Escapes a string’s HTML. Specifically, it makes these replacements:
< is converted to <
> is converted to >
' (single quote) is converted to '
" (double quote) is converted to "
& is converted to &
I can only guess why this didn't work. I figure it's because it didn't do anything about the white space. But I shouldn't speculate. I welcome any explanations you might have. As ever, understanding the way the machine thinks is better than any single, specific solution. Thanks.
[1] - https://docs.djangoproject.com/en/dev/ref/templates/builtins/#spaceless
[2] - https://docs.djangoproject.com/en/dev/ref/templates/builtins/#addslashes
[3] - https://docs.djangoproject.com/en/dev/ref/templates/builtins/#escape

Data style property to Jinja2 "invalid syntax"

I am trying to convert a bootstrap multiple selector into Jinja2, but whenever I add data-style property to the form field badges = SelectMultipleField('Categorii proiect', choices=[(1, 'January'), (2,'February')]) I get invalid syntax.
Bootstrap multiple selector
<select multiple class="form-control selectpicker" data-style="btn btn-link" id="exampleFormControlSelect2">
<option>January</option>
<option>February</option>
</select>
Jinja2 template that works
{{ form.badges(class="form-control selectpicker", id="exampleFormControlSelect2") }}
Jinja2 template that fails
{{ form.badges(class="form-control selectpicker", data-style="btn btn-link", id="exampleFormControlSelect2") }}
Any suggestion would be highly appreciated!
that's because data-style is not a valid parameter's name (there's - dash character) passed to the function.
a valid name has to be Alphanumeric, _ the underscore character is allowed, -, #, # .. are not allowed
to counter the invalid syntax error you need to pass that parameter and any extra parameters (if any) through the **kwrgs object like
{{ form.badges(class="form-control selectpicker", id="exampleFormControlSelect2", **{"data-style":"btn btn-link" }) }}

Is there a quick way to grab a the ending of current page url? Django

I have a page. www.example.com/chat/testuser
I want to grab the username (testuser). I'm doing this within the template in order to pass along in an ajax call.
Is there a simple way? Something like {{ request.path }} (this returns full path).
{{ request.user }} might do the trick.
Edit:
Here's are some solutions using request.path. Assuming it returns the string "/chat/testuser", you can use one of these filters:
{{ request.path|cut:"/chat/" }}
{{ request.path|slice:"6:" }}
to remove the unwanted portion.

{{ }} is not getting parsed by pyjade as expected with django

I have line of pyjade
a.js-track(data-track-data="{\"Job ID\":\"{{ job_details|get_or_na:'id' }}\",\"Job Title\":\"{{ job_details|get_or_na:'title' }}\",\"Company Name\":\"{{ job_details|get_or_na:'organization'|get_or_na:'name' }}\"}", data-track-dynamic-attrs="[\"Stakeholder\"]",href="{% url 'job_detail' job_details.title|slugify job_details.id %}")
which is being rendered as
<a href="/job/operations-manager/b1ac846e-6834-40c4-8bcf-122c093820b1/" data-track-data="{"Job ID":"{{ job_details|get_or_na:'id' }}","Job Title":"{{ job_details|get_or_na:'title' }}","Company Name":"{{ job_details|get_or_na:'organization'|get_or_na:'name' }}"}" data-track-dynamic-attrs="["Stakeholder"]" class="js-track">
I expect it to {{ }} being replaced by intended values rather than being rendered with html.
I am using 4.0.0 version of pyjade here as templating language.
Try to use #{value}
And for the conditions try to create a variable before the line so you can have more control
- var condition = inline_condition
p= condition