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

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

Related

Using AngularJS template tags in Django [duplicate]

I want to use AngularJS with Django however they both use {{ }} as their template tags. Is there an easy way to change one of the two to use some other custom templating tag?
For Angular 1.0 you should use the $interpolateProvider apis to configure the interpolation symbols: http://docs.angularjs.org/api/ng.$interpolateProvider.
Something like this should do the trick:
myModule.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
});
Keep in mind two things:
mixing server-side and client-side templates is rarely a good idea and should be used with caution. The main issues are: maintainability (hard to read) and security (double interpolation could expose a new security vector - e.g. while escaping of serverside and clientside templating by themselves might be secure, their combination might not be).
if you start using third-party directives (components) that use {{ }} in their templates then your configuration will break them. (fix pending)
While there is nothing we can do about the first issue, except for warning people, we do need to address the second issue.
you can maybe try verbatim Django template tag
and use it like this :
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
{% verbatim %}
<div ng-app="">
<p>10 is {{ 5 + 5 }}</p>
</div>
{% endverbatim %}
If you did separate sections of page properly then you can easily use angularjs tags in "raw" tag scope.
In jinja2
{% raw %}
// here you can write angularjs template tags.
{% endraw %}
In Django template (above 1.5)
{% verbatim %}
// here you can write angularjs template tags.
{% endverbatim %}
We created a very simple filter in Django 'ng' that makes it easy to mix the two:
foo.html:
...
<div>
{{ django_context_var }}
{{ 'angularScopeVar' | ng }}
{{ 'angularScopeFunction()' | ng }}
</div>
...
The ng filter looks like this:
from django import template
from django.utils import safestring
register = template.Library()
#register.filter(name='ng')
def Angularify(value):
return safestring.mark_safe('{{%s}}' % value)
So I got some great help in the Angular IRC channel today. It turns out you can change Angular's template tags very easily. The necessary snippets below should be included after your angular include (the given example appears on their mailing lists and would use (()) as the new template tags, substitute for your own):
angular.markup('(())', function(text, textNode, parentElement){
if (parentElement[0].nodeName.toLowerCase() == 'script') return;
text = text.replace(/\(\(/g,'{{').replace(/\)\)/g, '}}');
textNode.text(text);
return angular.markup('{{}}').call(this, text, textNode, parentElement);
});
angular.attrMarkup('(())', function(value, name, element){
value = value.replace(/\(\(/g,'{{').replace(/\)\)/, '}}');
element[0].setAttribute(name, value);
return angular.attrMarkup('{{}}').call(this, value, name, element);
});
Also, I was pointed to an upcoming enhancement that will expose startSymbol and endSymbol properties that can be set to whatever tags you desire.
I vote against using double parentheses (()) as template tag. It may work well as long as no function call is involved but when tried the following
ng:disabled=(($invalidWidgets.visible()))
with Firefox (10.0.2) on Mac I got a terribly long error instead of the intended logic. <[]> went well for me, at least up until now.
Edit 2012-03-29:
Please note that $invalidWidgets is deprecated. However I'd still use another wrapper than double braces. For any angular version higher than 0.10.7 (I guess) you could change the wrapper a lot easier in your app / module definition:
angular.module('YourAppName', [], function ($interpolateProvider) {
$interpolateProvider.startSymbol('<[');
$interpolateProvider.endSymbol(']>');
});
API docs.
You could always use ng-bind instead of {{ }}
http://docs.angularjs.org/api/ng/directive/ngBind
<span ng-bind="name"></span>
I found the code below helpful. I found the code here: http://djangosnippets.org/snippets/2787/
"""
filename: angularjs.py
Usage:
{% ng Some.angular.scope.content %}
e.g.
{% load angularjs %}
<div ng-init="yourName = 'foobar'">
<p>{% ng yourName %}</p>
</div>
"""
from django import template
register = template.Library()
class AngularJS(template.Node):
def __init__(self, bits):
self.ng = bits
def render(self, ctx):
return "{{%s}}" % " ".join(self.ng[1:])
def do_angular(parser, token):
bits = token.split_contents()
return AngularJS(bits)
register.tag('ng', do_angular)
If you use django 1.5 and newer use:
{% verbatim %}
{{if dying}}Still alive.{{/if}}
{% endverbatim %}
If you are stuck with django 1.2 on appengine extend the django syntax with the verbatim template command like this ...
from django import template
register = template.Library()
class VerbatimNode(template.Node):
def __init__(self, text):
self.text = text
def render(self, context):
return self.text
#register.tag
def verbatim(parser, token):
text = []
while 1:
token = parser.tokens.pop(0)
if token.contents == 'endverbatim':
break
if token.token_type == template.TOKEN_VAR:
text.append('{{')
elif token.token_type == template.TOKEN_BLOCK:
text.append('{%')
text.append(token.contents)
if token.token_type == template.TOKEN_VAR:
text.append('}}')
elif token.token_type == template.TOKEN_BLOCK:
text.append('%}')
return VerbatimNode(''.join(text))
In your file use:
from google.appengine.ext.webapp import template
template.register_template_library('utilities.verbatim_template_tag')
Source:
http://bamboobig.blogspot.co.at/2011/09/notebook-using-jquery-templates-in.html
You can tell Django to output {{ and }}, as well as other reserved template strings by using the {% templatetag %} tag.
For instance, using {% templatetag openvariable %} would output {{.
I would stick with a solution that uses both django tags {{}} as well angularjs {{}} with a either a verbatim section or templatetag.
That is simply because you can change the way angularjs works (as mentioned) via the $interpolateProvider.startSymbol $interpolateProvider.endSymbol but if you start to use other angularjs components like the ui-bootstrap you will find that some of the templates are ALREADY built with standard angularjs tags {{ }}.
For example look at https://github.com/angular-ui/bootstrap/blob/master/template/dialog/message.html.
If you do any server-side interpolation, the only correct way to do this is with <>
$interpolateProvider.startSymbol('<{').endSymbol('}>');
Anything else is an XSS vector.
This is because any Angular delimiters which are not escaped by Django can be entered by the user into the interpolated string; if someone sets their username as "{{evil_code}}", Angular will happily run it. If you use a character than Django escapes, however, this won't happen.

Template Contexts not recognized from external file (Django)

So, I've been editing a website and have many JavaScript functions that utilize the Contexts that the views.py file passes to the page. Until now, these functions have been contained in the base.html file and so have been loaded onto every page directly. However, to make things cleaner, I copy and pasted all the functions to an external .js file. Now, rather than use the contexts, the functions consider them to be literal strings.
Example:
$('#title').text('{{ event.name }}');
The above line will actually set the text of the element to say "{{ event.name }}" rather than the name of the event. Any ideas on how to fix this? I really don't want to keep these functions in the base file where they can be seen by anyone who inspects the page source.
It doesn't matter if you put your javascript functions in an external file or in your base.html it would still get exposed to the user. Only a minification of the file would actually help to trick the user from seeing the actual values but with javascript all your code is public.
Why you're having this problem is because when you rendered the javascript inline (in your base.html) you had access to the template context.
This is no longer the case and the Django template engine doesn't interpolate your {{ event.name }} anymore.
The problem you're facing as well is a good one. You should never mix and match javascript with Djangos template language or any template language for that matter and the only way of fixing it is to
a) start pulling the values from the DOM ie. render a proper DOM
b) to start to fetch the values from the server, traditionally using AJAX.
And the smallest example that I can muster at the moment is below:
Your view:
def my_django_view(request):
return HttpResponse(json.dumps({'meaningoflife':42}), mimetype='application/json')
Your HTML
<input type="hidden" id="myMeaning" value="{{ meaningoflife }}" />
Your javascript
var meaning = document.querySelector('#myMeaning').value;
alert(meaning); //should alert 42.
In your view you return some form of render_to_response which takes a template argument and a context argument. What the render_to_response function does is read your template, and replace all {{ placeholders }} with the values passed via the context dictionary.
Templates are essentially a complex version of this
"""
<h1>{{ person.name }}</h1>
<p>{{ person.phone_number }}</p>
""".format(person)
The problem is the templating engine does not know files specified by a scripts src attribute is actually a Django template. To fix this don't use the script src attribute. Instead do something like this.
<!--base.html-->
<h1>Site Title</h1>
<p>Some content</p>
<script>
{% include 'jsfile.js' %}
</script>
Using the include statement should do the trick.

Add a newline after each closing html tag in web2py

Original
I want to parse a string of html code and add newlines after closing tags + after the initial form tag. Here's the code so far. It's giving me an error in the "re.sub" line. I don't understand why the regex fails.
def user():
tags = "<form><label for=\"email_field\">Email:</label><input type=\"email\" name=\"email_field\"/><label for=\"password_field\">Password:</label><input type=\"password\" name=\"password_field\"/><input type=\"submit\" value=\"Login\"/></form>"
result = re.sub("(</.*?>)", "\1\n", tags)
return dict(form_code=result)
PS. I have a feeling this might not be the best way... but I still want to learn how to do this.
EDIT
I was missing "import re" from my default.py. Thanks ruakh for this.
import re
Now my page source code shows up like this (inspected in client browser). The actual page shows the form code as text, not as UI elements.
<form><label for="email_field">Email:</label>
<input type="email" name="email_field"/><label
for="password_field">Password:</label>
<input type="password" name="password_field"/><input
type="submit" value="Login"/></form>
EDIT 2
The form code is rendered as UI elements after adding XML() helper into default.py. Thanks Anthony for helping. Corrected line below:
return dict(form_code=XML(result))
FINAL EDIT
Fixing the regex I figured myself. This is not optimal solution but at least it works. The final code:
import re
def user():
tags = "<form><label for=\"email_field\">Email:</label><input type=\"email\" name=\"email_field\"/><label for=\"password_field\">Password:</label><input type=\"password\" name=\"password_field\"/><input type=\"submit\" value=\"Login\"/></form>"
tags = re.sub(r"(<form>)", r"<form>\n ", tags)
tags = re.sub(r"(</.*?>)", r"\1\n ", tags)
tags = re.sub(r"(/>)", r"/>\n ", tags)
tags = re.sub(r"( </form>)", r"</form>\n", tags)
return dict(form_code=XML(tags))
The only issue I see is that you need to change "\1\n" to r"\1\n" (using the "raw" string notation); otherwise \1 is interpreted as an octal escape (meaning the character U+0001). But that shouldn't give you an error, per se. What error-message are you getting?
By default, web2py escapes all text inserted in the view for security reasons. To avoid that, simply use the XML() helper, either in the controller:
return dict(form_code=XML(result))
or in the view:
{{=XML(form_code)}}
Don't do this unless the code is coming from a trusted source -- otherwise it could contain malicious Javascript.

Is is possible to html encode output in AppEngine templates?

So, I'm passing an object with a "content" property that contains html.
<div>{{ myobject.content }}</div>
I want to be able to output the content so that the characters are rendered as the html characters.
The contents of "conent" might be: <p>Hello</p>
I want this to be sent to the browser as: &amplt;p&ampgt;Hello&amplt;/p>
Is there something I can put in my template to do this automatically?
Yes, {{ myobject.content | escape }} should help (assuming you mean Django templates -- there's no specific "App Engine" templating system, GAE apps often use the Django templating system); you may need to repeat the | escape part if you want two levels of escaping (as appears to be the case in some but not all of the example you supply).
This is Django's django.utils.html.escape function:
def escape(html):
"""Returns the given HTML with ampersands, quotes and carets encoded."""
return mark_safe(force_unicode(html).replace('&', '&').replace('<', '&l
t;').replace('>', '>').replace('"', '"').replace("'", '''))
Also, see here.

Using string literals as parameters to template tags in Django templates

One of the things I find myself doing often is passing string literals as parameters to template tags or functions; for instance:
{% url my-url 'my_param' %}
Unfortunately, the django template engine doesn't let you do this. So I find myself doing this a lot in my view code:
my_context_dict['MY_PARAM'] = 'my_param'
and then in my view code:
{% url my-url MY_PARAM %}
Or creating a series of URL mappings, which I personally try to avoid.
Is it possible to use a string literal in Django templates? Or possibly a more elegant solution? I haven't seen anything on here or in the documentation.
This feels wrong but is right.
text
The nested ""'s don't seem like they should work. They do. The Django {% %} material is simply pulled out of the HTML without regard for surrounding context. So the "duplicated" "'s aren't really duplicated at all.
Use double quotes instead of single quotes:
{% url my_view "my_param" %}
Very wierd - I have a django project that uses single quotes to pass a string value and it functions just fine.
<a href="{% url categories 'vendor' %}"</a>
<a href="{% url categories 'crew' %}"</a>
On further investigation it turns out this has changed in django 1.5. It now requires the quotes even around the url pattern name.