Trouble using mako templates - python

I'm trying to do something that I imagine must be trivial in mako, but I just can't figure out how I should procede and I'm finding the documentation pretty useless. I'm quite familiar with Python and DTL, but I just don't understand why this code is throwing a syntax error.
Basically, all I want to do is take in a datum object (just a small dictionary) and differently generate a link based on where the request is coming from. I know it would be trivial to do this in straight python and pass it in as context, but I'm really trying to warm up to mako. Any help would be hugely appreciated.
<%def name="courseware_link(datum)">
% if courseware in ${request.url}:
<a href=${request.url}[:${request.url}.find("courseware")+len("courseware")+1]+datum["url"]>
% else:
<a href=${request.host}+"/courses/"+datum["org"]+"/"+datum["course_ids"]+"/#/courseware/"+datum["url"]
% endif
</%def>
More specifically the syntax error is this:
(SyntaxError) invalid syntax (<unknown>, line 1) (u'if courseware in ${request.url}:pass') in file '/file' at line: 70 char: 1
and line 70 is the second line % if courseware...

You are mixing ${} with regular python in the if conditional and both a tags. Also, you cannot nest ${} inside ${}. You should probably refactor this code to be either out of the template or into a <% %> block, but something like this should work:
%if "courseware" in request.url:
<a href="${request.url[:request.url.find('courseware')+len('courseware')+1]+datum['url']}">
%else:
<a href="${request.host + '/courses/' + datum['org'] + '/' + datum['course_ids'] + '/#/courseware/' + datum['url']}">
%endif
Here is a refactored version:
<%def name="courseware_link(datum)">
<%
if "courseware" in request.url:
url = request.url[:request.url.find("courseware")+len("courseware")+1]
url += datum["url"]
else:
url = request.host + "/courses/" + datum["org"] + "/"
url += datum["course_ids"] + "/#/courseware/" + datum["url"]
%>
<a href="${url}">
</%def>
Furthermore you might want to use a routing package to generate your urls instead of building them manually like this, Django should provide something to automatically construct urls.

Related

How to pass value from python to html form using flask/jinja

So I first create an array of all folders in a specific directory, I then pass that to my html file.
def test_yt_vid():
mylist = os.listdir(WD+r"static/"+YOUTUBE_FOLDER)
full_path = (WD+YOUTUBE_FOLDER)
return dict(mylist=mylist, full_path=full_path)
Next I look through that array to find what file has been selected.
<select name=list id="videolist" method="GET" action="/">
{% for mylist in mylist %}
<option value= "{{mylist}}" SELECTED>{{mylist}}</option>"
{% endfor %}
</select>
Next I use JS to get the specific value into a variable
$('#videolist').change(function () {
//console.log($("#videolist").val());
var fileInput = $("#videolist").val())};
So The problem is here, I'm not sure how I would go about passing that value into the following jinja code
<video id="videotesting1" class="video" width="300" height="240" autoplay loop controls="true">
<source src="{{url_for('static',filename='videoTest/' + 'testVid.mp4')}}" type="video/mp4">
</video >
I'm trying to replace 'testVid.mp4' with the variable fileInput from the JS, I tried using $("#videotesting1").attr("src","{{url_for('static',filename='videoTest/'" + fileInput +")}}");'
But no luck so far.
This is different to "How do you change video src using jQuery?" because I am trying to pass a jinja variable to HTML using js.
You have some wrong closed quotes. Take a look at filename, where you set 'videoTest/' plus some variable value (e.g x), which results in 'videoTest/'x. Do you notice it? The single quote closed after videoTest should appear after the variable fileInput. The correct way would be:
$("#videotesting1").attr("src","{{url_for('static',filename='videoTest/" + fileInput + "')}}");
When you modify the src, has by inspect element the src changed, but the desired video isn't being played? If so, try:
$("#videotesting1").load()
Take a look at what load does # JQuery docs.
Figure out the problem, the file name has to go outside the jinja code because it doesnt get rendered by jinja for some reason when the event happens.
$("#videotesting1").attr("src","{{url_for('static',filename='videoTest/')}}" + fileInput);

Multiline posting in HTML

I'm totally new at web development and I am currently trying to create a little website. The goal of this site, is to show random quotes of some of my teachers. The main pages are actually working just fine (I can get random quotes of my whole database, and random quotes from every teacher). But, I wanted to show all the quotes on the same page, and it happens they just appear all on the same line... And it's quite embarrassing...
In my python code, I used "\n" between each quote, so each new one started on a new line. But, on my HTML code, when I pass this string, it seems to have no effect I all the quotes just follow themselves on one line....
I'm using a Flask application, and a python class:
for i in range(2, max):
inte = inte + citation.ClasseCitations('Classe/citations.json','Classe/profs.json', prof, i).corps + ' \n '
return render_template("integrale.html", citation=inte, auteur=prof)
In my HTML file, I use citation like this:
<p>{{ citation }}</p>
Try this :
for i in range(2, max):
inte = inte + citation.ClasseCitations('Classe/citations.json','Classe/profs.json', prof, i).corps + ' <br/> '
return render_template("integrale.html", citation=inte, auteur=prof)
I'm not able to comment, but try it with
<br/>
instead of
\n
this could work.
I'd try using a list object within your flask-app.
Then in your html:
{% for quote in quotes %}
{{quote}} <br>
{% endfor %}
More on jinja2's for-loops
http://jinja.pocoo.org/docs/2.9/templates/#for-loop

Extending Jinja's {% trans %} to use JavaScript variables

I'd like to extend the behaviour of trans by rendering variables not as as values from the context, but instead as html (without using the context). My aim is to be able to populate those variables on the client through JavaScript.
Jinja as it seems doesn't allow for a great deal of customisation of this kind or I'm just unable to find the right hooks.
Here's what I'd like to achieve:
{% etrans name=username %}
My name is {{ name }}
{% endetrans %}
This should render to:
My name is <span id='#username'></span>
Of course, I could just use the normal {% trans %} directive and pass my html code to template.render(html_code_params), but that would require to have them defined in the template and the rendering code which I'd like to avoid.
Here's what I got so far (not much) which allows for a new etrans tag and the ability to use whatever goodies InternationalizationExtension has to offer.
from jinja2.ext import InternationalizationExtension
from jinja2.runtime import concat
class JavaScriptVariableExtension(InternationalizationExtension):
tagname = 'etrans'
tags = set([tagname])
def _parse_block(self, parser, allow_pluralize):
"""Parse until the next block tag with a given name.
Copy from InternationalizationExtension, as this uses hardcoded
`name:endtrans` instead of relying on tag name
"""
referenced = []
buf = []
while 1:
if parser.stream.current.type == 'data':
buf.append(parser.stream.current.value.replace('%', '%%'))
next(parser.stream)
elif parser.stream.current.type == 'variable_begin':
next(parser.stream)
name = parser.stream.expect('name').value
referenced.append(name)
buf.append('%%(%s)s' % name)
parser.stream.expect('variable_end')
elif parser.stream.current.type == 'block_begin':
next(parser.stream)
# can't use hardcoded "endtrans"
# if parser.stream.current.test('name:endtrans'):
if parser.stream.current.test('name:end%s' % self.tagname):
break
elif parser.stream.current.test('name:pluralize'):
if allow_pluralize:
break
parser.fail('a translatable section can have only one '
'pluralize section')
parser.fail('control structures in translatable sections are '
'not allowed')
elif parser.stream.eos:
parser.fail('unclosed translation block')
else:
assert False, 'internal parser error'
return referenced, concat(buf)
i18n_extended = JavaScriptVariableExtension
I don't mind overloading more methods (although the reason for above one should perhaps fixed upstream).
Stepping through the code is quite an interesting adventure. However, I hit a snag and am interested if anyone can give some advice.
The problem I see is that during the compilation, the function context.resolve() gets baked into the compiled code. jinja2.jinja2.compiler.CodeGenerator doesn't really allow any different handling here (correct me if I'm wrong). Ideally, I would define another node (for the variable) and this node would handle the way it's dealt with during compilation, but I don't see how this is possible. I might be too focussed on this as a solution, so perhaps someone can provide alternatives.
As suggested by #Garrett's comment, a much easier solution is to pass in a function to the template renderer that interpolates the variables. In my case, my target client-side framework is Angular, but this also works for any JS variables that you want to use within a {% trans %} environment. Here are the building blocks:
def text_to_javascript(string):
# modify as needed...
return "<span>{{ %s }}</span>" % string
def render():
tmpl = jinja_env.get_template(template_filename)
return tmpl.render({'js': text_to_javascript})
And this how I make use of it in the template file:
{% trans username=js('user.name') %}
My name is {{ username }}
{% endtrans %}
In the Angular controller, the variable user is bound to the $scope like so:
$scope.user = {'name': 'Bugs Bunny'}

Sleek way of un/commenting out html tags in markdown

I'm trying to find a nice way of wrapping html tags in html comments without writing 5 functions and 50 lines of code. Using an example code :
<section class="left span9">
### Test
</section>
I need to transform it to :
<!--<section class="left span9">-->
### Test
<!--</section>-->
I have a regex to find the tags
re.findall('<.*?>', str)
but in the last years I wasn't using lambdas too often so now I'm having a hard time getting it to work.
btw any ideas for the reverse of this process - decommenting the tags ?
You can comment/uncomment using simple replace like this
myString = '<section class="left span9">'
print myString.replace("<", "<!--<").replace(">", ">-->")
print myString.replace("<!--", "").replace("-->", "")
Output:
<!--<section class="left span9">-->
<section class="left span9">
Note: This works because, a valid HTML document should have < and > only in the HTML tags. If they should appear, as they are, in the output, they have to be properly HTML escaped with > and <
Ok, so temporarily I've ended up using two functions and re.sub for that :
def comment(match):
return '<!--'+match.group(0)+'-->'
def uncomment(html):
return html.replace('<!--', '').replace('-->', '')
commented_html = re.sub('<.*?>', comment, html_string)
uncommented_html = uncomment(commented_html)

"%" character cause Error in strings substitution with locals()

I try to substitue strings with variables using locals() in python but I can find a way to use the % character inside the string without error. Here is a concrete example :
color = colors_generator() #the function return a color
html = """<html><head>
<style>#square{color:%(color)s;width:100%;height:100%;}</style>
</head> <body> <div id="square"> </div>
</body></html>""" % locals()
print "Content-Type: text/html\n"
print html
Result : TypeError: not enough arguments for format string
The problem is the % character in 100%. How can I escape it?
escape % with %
html = """<html><head>
<style>#square{color:%(color)s;width:100%%;height:100%%;}</style>
</head> <body> <div id="square"> </div>
</body></html>""" % locals()
Virhilo has already answered your direct question, but if you find you are building quite big/complicated templates it might be worth looking at a full blown template engine instead:
http://docs.python.org/library/string.html
http://jinja.pocoo.org/
http://www.makotemplates.org/

Categories