Data style property to Jinja2 "invalid syntax" - flask

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" }) }}

Related

How to add date-time arguments to django URL tag

I am trying to get this URL in my template.
path('show_date/<int:year>/<int:month>/<int:date>/',show_date,name='show_date'),
My template
<a href="{%url 'diary:show_date'{{date|date:'Y'}} {{date|date:'m'}} {{date|date:'j'}}
%}">{{date}}</a>
returns this error
Could not parse some characters: 'diary:show_date'|{{date||date:'Y'}}
Please help me fix this issue
You shouldn't add the double braces when you use a filter on a variable in a tag, you can use the filters without them
{{ date }}

WTForms use quotes in Jinja2

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

template tag 'add' requires 2 arguments, 1 provided

I have used add before but getting this strange error this time:
django.template.exceptions.TemplateSyntaxError: add requires 2 arguments, 1 provided
with the following code:
{% render_field form.full_name type="hidden" class="form-control" \
value=user.first_name|add:"something" %}
However I don't get the error if I move add to with:
{% with fullname=user.first_name|add:"something"%}
{% render_field form.full_name type="hidden" class="form-control" \
value=fullname %}
{% endwith %}
So it looks like you are using the render_field template tag from https://github.com/jazzband/django-widget-tweaks/
The render_field is implemented as an Advanced Custom Tag that is implemented by the package maintainer, that sadly does not support filters in it's argument list.
A simple_tag or inclusion_tag on the other hand; which is defined by Django; does support other filters within its argument list.
What I mean to say is that this will work if it were a simple_tag or inclusion_tag
{% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}
However, if my_tag was registered as an advanced custom tag; supporting filters within the arguments is up to the implementation.
As I see from the implementation of the render_field code; it is an advanced custom tag and filters support appears to be broken:
#register.tag
def render_field(parser, token):
"""
Render a form field using given attribute-value pairs
Takes form field as first argument and list of attribute-value pairs for
all other arguments. Attribute-value pairs should be in the form of
attribute=value or attribute="a value" for assignment and attribute+=value
or attribute+="value" for appending.
"""
error_msg = '%r tag requires a form field followed by a list of attributes and values in the form attr="value"' % token.split_contents()[0]
try:
bits = token.split_contents()
tag_name = bits[0]
form_field = bits[1]
attr_list = bits[2:]
except ValueError:
raise TemplateSyntaxError(error_msg)
form_field = parser.compile_filter(form_field)
set_attrs = []
append_attrs = []
for pair in attr_list:
match = ATTRIBUTE_RE.match(pair)
if not match:
raise TemplateSyntaxError(error_msg + ": %s" % pair)
dct = match.groupdict()
attr, sign, value = \
dct['attr'], dct['sign'], parser.compile_filter(dct['value'])
if sign == "=":
set_attrs.append((attr, value))
else:
append_attrs.append((attr, value))
return FieldAttributeNode(form_field, set_attrs, append_attrs)
Specifically; the pairs of key=value are stored in attr_list in the code and run against the ATTRIBUTE_RE regex matching
Then the filter is run using parser.compile_filter(dct['value']), however dct['value'] is 'user.first_name|add:"' -> the text "something" is lost here. My guess is that the ATTRIBUTE_RE regex needs to be improved to support this.
>>> ATTRIBUTE_RE.match('value=user.first_name|add:"something"')
<_sre.SRE_Match object at 0x7f8617f5a160>
>>> match.groupdict()
{'attr': 'value', 'value': 'user.first_name|add:"', 'sign': '='}
As you can see, the value key is broken and missing the full text you supplied; which breaks the add filter downstream.
This is exactly the use-case that the with tag is meant to solve; as it abstracts the filtering process to a level above; and you can pass the new variable to your custom render_field tag nicely.
References:
How to use a template filter on a custom template tag?
https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/#advanced-custom-template-tags

Django Form Field Problems with Cloudinary's CloudinaryImage Class

I've got a Django form that is displaying a ClearableFileInput for a CloudinaryImage (from Cloudinary). Things are working great, except when I display the form field, I get a mangled href in the anchor element:
Currently: <cloudinary.CloudinaryImage object at 0x10b3f4ad0> <input type="checkbox" name="logo-clear" id="logo-clear_id" /> <label for="logo-clear_id">Clear</label><br />Change: <input id="id_logo" type="file" name="logo" class="span4" />
Here is the template code I am using:
<div class="fieldWrapper">
<label for="id_logo"><h3>{{ form.logo.label }}:</h3></label>
{{ form.logo|add_class:"span4" }}
<p>{{ form.logo.help_text }}</p>
</div>
The add_class part come from django-widget-tweaks. I've taken the add_class part out with no change in the output.
Here is my form definition:
class OrganizationFormTheme(forms.ModelForm):
pass
class Meta:
fields = ('logo',)
model = Organization
It looks like Django is having problems with the CloudinaryImage's url function. I suspect it is looking for a simple property rather than a function.
Any suggestions on how to handle this? Should I subclass CloudinaryImage and rewrite the url function somehow?
Indeed there was a conflict between the url function and the url property.
We've changed the function to be build_url instead of url.
In addition, you can specify transformation parameters as the url_options parameter when calling the constructor of CloudinaryImage. Then you can use the url property for getting the full Cloudinary URL.
The fix is available in the latest release of the Python library: http://pypi.python.org/pypi/cloudinary

Django trans tag within a default filter

Does anyone know how this could properly be written in Django?
{{ mu.expiry_date|default:"{% trans 'Free User' %}"}}
Obviously, the above does not work since it contains a tag within a tag's filter.
Templates have an underscore syntax for translation also:
{{ mu.expiry_date|default:_("Free User")}}