Python flask - Encoding links based on JSON varibale - python

I'm creating an application in Python flask and I'm struggling to encode my links. In my HTML template I'm calling data from JSON and based on a variable from JSON, I want to create a link to another page but the variables that have "space" in them, only take the first word and the link doesn't work as it should.
This is my JSON:
[
{
"team":"AFC Bournemouth"
},
{
"team":"Arsenal"
}
]
And this is my python:
#app.route('/<team>/')
def artist(team):
json_data=open('static/data.json').read()
data= json.loads(json_data)
urllib.quote_plus(data.team)
return render_template("team.html", team=team)
I'm trying to use "urllib.quote_plus" but I get an error
AttributeError: 'list' object has no attribute 'team'
I don't know how to fix it.
And this is my loop in html:
{% for data in results %}
<div class="team">
<p><a href=/{{ data.team }}>{{ data.team }}</a></p>
</div>
{% endfor %}
Before I used "urllib.quote_plus" the link for "Arsenal" worked perfect, but for "AFC Bournemouth" it only took the word "AFC".

That is strange that it is working correctly for "Arsenal". Actually you should iterate over the "data" because it is a list
Example:
#app.route('/<team>/')
def artist(team):
json_data=open('static/data.json').read()
data= json.loads(json_data)
data = [{'team': urllib.quote_plus(team['team'])} for team in data]
return render_template("team.html", results=data)
Another thing is that in render_template you are sending variable team and not results (changed in my example). This way it should be fine and work with your Jinja template.
Edit: changed list comprehension

Related

Issue with displaying non-English characters in flask application in bootstrap table [duplicate]

I am trying to pass data as JSON from a Flask route to a Jinja template rendering JavaScript. I want to iterate over the data using JavaScript. The browser shows SyntaxError: Unexpected token '&'. Expected a property name. when JSON.parse is called on the rendered data. How do I use rendered JSON data in JavaScript?
var obj = JSON.parse({{ data }})
for (i in obj){
document.write(obj[i].text + "<br />");
}
def get_nodes(node):
d = {}
if node == "Root":
d["text"] = node
else:
d["text"] = node.name
getchildren = get_children(node)
if getchildren:
d["nodes"] = [get_nodes(child) for child in getchildren]
return d
tree = get_nodes("Root")
return render_template("folder.html", data=tree)
If I just put {{ data }} in the HTML part, what I see looks correct.
{'text': 'Root', 'nodes': [{'text': u'Prosjekt3'}, {'text': u'Prosjekt4', 'nodes': [{'text': u'mappe8'}]}]}
Flask's Jinja environment automatically escapes data rendered in HTML templates. This is to avoid security issues if the dev tries to render untrusted user input.
Since you are passing a Python object to be treated as JSON, Flask provides the tojson filter which automatically dumps the data to JSON and marks it safe.
return render_template('tree.html', tree=tree)
var tree = {{ tree|tojson }};
When you just look at the data rendered in HTML, it looks correct because the browser displays the escaped symbols as the real symbols (although in this case you're seeing the string representation of a Python dict, not JSON, so there's still some issues like u markers).
Previous versions of Flask didn't mark the dumped data safe, so you might come across examples like {{ tree|tojson|safe }}, which isn't required anymore.
If you're not rendering JSON (or you already dumped the JSON to a string), you can tell Jinja that data is safe to render without escaping by using the safe filter.
# already dumped to json, so tojson would double-encode it
return render_template('tree.html', tree=json.dumps(tree))
var tree = {{ tree|safe }};
You can also wrap the string in Markup before rendering it, it's equivalent to the safe filter.
# already dumped and marked safe
return render_template('tree.html', tree=Markup(json.dumps(tree)))
var tree = {{ tree }};
If you're not passing this data to JavaScript, but using it in Jinja instead, you don't need JSON. Pass the actual Python data, don't call tojson on it, and use it as you would any other data in the template.
return render_template('tree.html', tree=tree)
{% for item in tree %}
<li>{{ item }}</li>
{% endfor %}
I could archive it using the following code sample.
<script>
console.log(JSON.parse({{json|safe}}))
</script>
The problem is that your server returns not JSON, but rendered HTML, which escapes some of the symbols with & notation.
Instead of using
return render_template("folder.html", data=tree)
try
return flask.jsonify(**tree)

jinja2 include file with variable

I'm trying to refacto some pretty heavy template with jinja2 and I'm stucked on an include.
This is the behaviour i'm expecting :
<h1>{{ key }} </h1>
{% set file = key | include_text %}
{% include file %}
The custom filter returns a string like this one ::
texts/my_include.html
But instead I got this error:
jinja2.exceptions.TemplatesNotFound: Tried to select from an empty list of templates
Some hack I've already tried :
Place the templates in the same folder and remove the 'texts/' from the returned string
Add the path in the Env loader
But it keeps sending this error
I'm now wondering if jinja2 allows this implementation or if I'll have to keep this template the way it was (even if it takes a very long time to be generated).
Does someone know about some trick here ?
Well, for those who eventually met this problem in the futur, I've solved it by removing the unecessary single quotes and by sending some empty file from my custom filter when the condition is not verified... (my mistake)
Here is my custom filter :
#environmentfilter
def include_text(ctx, key):
res_dict = {
'key_value_1' : 'file_name_1',
'key_value_2' : 'file_name_2'
}
try:
return "texts/" + res_dict[key] + ".html"
except KeyError:
return "texts/empty.html"
Now, the first solution I was trying works fine.

trim() function in Python using Flask

Is there a function like trim() in python?
i use Flask miniframework and it doesn't accept:
selected_student = (request.args.get('student_form')).strip()
its error: AttributeError: 'NoneType' object has no attribute 'strip'
selected_student.replace(" ", "")
its error: AttributeError: 'NoneType' object has no attribute 'replace'
i need a function like trim() without coding a class/subclass or javascript
You're seeing the errors that you are seeing because there is no data being passed from your form to the Flask server. Your use of request is returning a None value type as opposed to a str.
You posted the following HTML mark up for your form:
<form action="/student" method='POST'>
<select name="student_form ">
{% for student in students_list %}
<option value="{{student}}">{{student}}</option>
{% endfor %}
</select>
<input type='submit' value='submit' />
</form>
So therefore you're going to need somewhere for Flask to pick up this data on the server side, for example:
#app.route('/student', methods=['POST'])
def receive_student_form_data:
my_selection = str(request.form.get('student_form')).strip()
print(my_selection)
Just to clarify why I've made my method in this way: I notice that you're using request.args.get() in order to retrieve the value sent by the form. This is incorrect.
request.args is used to retrieve key / value pairs from the URL.
request.form is used to retrieve key / value pairs from a HTML form.
So I'd suggest that you should use request.form.get('student_form') instead. If you really want to be certain that it is being cast as a str when retrieved by your Flask server, then you can cast it as a str as follows:
str(request.form.get('student_form'))
Then, as has been suggested by a few people already, you can use the .strip() method to remove any trailing spaces.
There is a strip() method. The error you get is because you are trying to run it on a NoneType object. You need to run it on a string object.
>>> s = 'some string '
>>> s.strip()
'some string'
There is also replace for strings:
>>> s.replace('some', 'many')
'many string '
The issue you encounter is related to something else. You end with a None object instead of what you are trying to get.

How do I use Jinja2 filter on a block with argumets

I am trying to use jinja2 templates. I have custom filter called highlight, that takes string and language name and passes them to pyhments for code highlightning. I am trying to use it like this:
{% filter highlight("python") %}
import sys
def main():
pass
{% endfilter %}
But I get this error:
AttributeError: 'str' object has no attribute 'get_tokens'
Then I tried this:
{% filter highlight "python" %}
It does not work either.
There might be a trick via set block filtering and then pasting it back via {{ ... }}, but this technique is not merged in master source code yet, and seems too hacky for me.
So, is that even possible currently, or I am just doing it wrong?
EDIT: Here is filter:
#jinja2.contextfilter
def highlight(context, code, lang):
print("HIGHLIGHT")
print(code)
return jinja2.Markup(pygments.highlight(code, lexer=lang, formatter='html'))
I am an idiot, that was pygments error. By some mistake, I didn't see that last entry in stacktrace was from there.
You should use:
pygments.highlight(
code,
lexer=pygments.lexers.get_lexer_by_name(lang),
formatter=pygments.formatters.get_formatter_by_name('html')
)
instead of:
pygments.highlight(code, lexer=lang, formatter='html')

What is the Flask version of Django's render_to_string function?

So, I'm trying to learn TDD for Flask, by translating this code to Flask. I've been trying to find how to render a template to a string for a while now. Here is what I have tried:
render_template(...)
render_template_string(...)
make_response(render_template(...)).data
and none of them seem to work.
The error in each case seems to be
"...templating.py", line 126, in render_template
ctx.app.update_template_context(context)
AttributeError: 'NoneType' object has no attribute 'app'
in templating.py's render_template function.
My test code is as follows:
def test_home_page_can_save_POST_request(self):
with lists.app.test_client() as c:
c.get('/')
rv = c.post('/', data = {'item_text':"A new list item"})
# This test works
self.assertIn("A new list item", rv.data)
# This test doesn't
self.assertEqual(rv.data,flask.make_response(flask.render_template('home.html',new_item_text='A new list item')).data)
with home.html as follows:
<html>
<body>
<h1>Your To-Do list</h1>
<form method="POST">
<input name="item_text" id="id_new_item" placeholder="Enter a to-do item" />
</form>
<table id="id_list_table">
<tr><td>{{ new_item_text }}</td></tr>
</table>
</body>
</html>
Edit: I've added more files, because the error may be unrelated to the actual function used. I'm using exactly what Celeo has suggested in his answer.
Celeo is correct, but there are two additional things to consider (one of which is peculiar to the render_template function):
First, it looks like you have an indentation problem in your revised function. It looks like you're calling rv.data outside of the "with" statement. The "assertEqual" statement should be within the same block/indentation-level as the "assertIn" statement. (It looks like you've placed it outside of the block at the moment.)
Second -- and more importantly -- the render_template function in flask adds newline characters at the beginning and end of the outputted HTML. (You can verify this from the python interactive shell by printing the following command to stdout:
flask.render_template('home.html',new_item_text='A new list item').data # adds '\n' at start & end
The output you'll get will have newline characters ("\n") at the beginning and end of the output.
Therefore, you should try stripping the output with the strip() function as shown below:
def test_home_page_can_save_POST_request(self):
with lists.app.test_client() as c:
c.get('/')
rv = c.post('/', data = {'item_text':"A new list item"})
self.assertIn("A new list item", rv.data)
# Suggested Revision
self.assertEqual(rv.data,flask.make_response(flask.render_template('home.html',new_item_text='A new list item')).data.strip())
That should hopefully do the trick.
You're on the right path with make_response:
response = make_response(render_template_string('<h2>{{ message }}</h2>', message='hello world'))
Then,
response.data
is
<h2>hello world</h2>
That response object is documented here.

Categories

Resources