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

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.

Related

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.

Python flask - Encoding links based on JSON varibale

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

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

Using '#notfound_view_config' in Pyramid with 'renderer'

I'm unable to get my specified template to render when using #notfound_view_config as described in Pyramid Docs - Using Hooks.
views.py:
#notfound_view_config(renderer='templates/notfound.pt')
def notfound(request):
return Response('Not Found, dude', status='404 Not Found')
templates/notfound.pt:
<html xmlns="http://www.w3.org/1999/xhtml"
metal:use-macro="base">
<tal:block metal:fill-slot="content">
<!-- Example row of columns -->
<div class="row">
<div class="span12">
<h1>Error:</h1>
<p>Uh, oh... you snagged an error:</p>
<pre>"${request}"</pre>
<p>You can return to the homepage if you wish.</p>
</div>
</div>
</tal:block>
</html>
When hitting a page that doesn't exist, I see the message "Not Found, dude" on a blank page, but I expected to see my template with "Uh, oh... you snagged an error!" followed by the request information.
I suspect I'm reading this wrong:
The notfound_view_config constructor accepts most of the same
arguments as the constructor of pyramid.view.view_config. It can be
used in the same places, and behaves in largely the same way, except
it always registers a not found exception view instead of a ‘normal’
view.
On one hand, it seems like I should be able to specify 'renderer' as a parameter since it's supported in pryamid.view.view_config. On the other hand, it sounds like it's always loading the [not found exception view][3], regarless of the 'renderer' option.
Really, my ultimate question (and goal) is, how do I display/render my template whenever a page is not found?
The renderer-view relationship is always the same in Pyramid. If you return a Response object, then your declared renderer is bypassed. This allows you to do things like if submitted: return HTTPFound(location=...) else: return {}. If you'd like to affect the response object and still use your renderer, then return the required dict and mutate request.response, the response object that is used for all renderers.
#notfound_view_config(renderer='templates/notfound.pt')
def notfound(request):
request.response.status = 404
return {}

Categories

Resources