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 {}
Related
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.
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.
I'm doing a presentation of the sample model (Footer.objects.all()) and send the result to the template. deduce the template through the cycle for:
{% for entrie in copyright_obj%}
{{entrie.copyright}}
{% endfor%}
The resulting data are displayed on the screen
but if I do so sample Footer.objects.all()[0], I get a message on the screen error
Exception Type: TypeError
Exception Value:
'Footer' object is not iterable
please tell me how can I print the data in this case?
The statement
Footer.objects.all()[0]
don't have any problem.
The thing is you're using the same template and you're trying to iterate over a single Footer objet.
In flask, I can do this:
render_template("foo.html", messages={'main':'hello'})
And if foo.html contains {{ messages['main'] }}, the page will show hello. But what if there's a route that leads to foo:
#app.route("/foo")
def do_foo():
# do some logic here
return render_template("foo.html")
In this case, the only way to get to foo.html, if I want that logic to happen anyway, is through a redirect:
#app.route("/baz")
def do_baz():
if some_condition:
return render_template("baz.html")
else:
return redirect("/foo", messages={"main":"Condition failed on page baz"})
# above produces TypeError: redirect() got an unexpected keyword argument 'messages'
So, how can I get that messages variable to be passed to the foo route, so that I don't have to just rewrite the same logic code that that route computes before loading it up?
You could pass the messages as explicit URL parameter (appropriately encoded), or store the messages into session (cookie) variable before redirecting and then get the variable before rendering the template. For example:
from flask import session, url_for
def do_baz():
messages = json.dumps({"main":"Condition failed on page baz"})
session['messages'] = messages
return redirect(url_for('.do_foo', messages=messages))
#app.route('/foo')
def do_foo():
messages = request.args['messages'] # counterpart for url_for()
messages = session['messages'] # counterpart for session
return render_template("foo.html", messages=json.loads(messages))
(encoding the session variable might not be necessary, flask may be handling it for you, but can't recall the details)
Or you could probably just use Flask Message Flashing if you just need to show simple messages.
I found that none of the answers here applied to my specific use case, so I thought I would share my solution.
I was looking to redirect an unauthentciated user to public version of an app page with any possible URL params. Example:
/app/4903294/my-great-car?email=coolguy%40gmail.com to
/public/4903294/my-great-car?email=coolguy%40gmail.com
Here's the solution that worked for me.
return redirect(url_for('app.vehicle', vid=vid, year_make_model=year_make_model, **request.args))
Hope this helps someone!
I'm a little confused. "foo.html" is just the name of your template. There's no inherent relationship between the route name "foo" and the template name "foo.html".
To achieve the goal of not rewriting logic code for two different routes, I would just define a function and call that for both routes. I wouldn't use redirect because that actually redirects the client/browser which requires them to load two pages instead of one just to save you some coding time - which seems mean :-P
So maybe:
def super_cool_logic():
# execute common code here
#app.route("/foo")
def do_foo():
# do some logic here
super_cool_logic()
return render_template("foo.html")
#app.route("/baz")
def do_baz():
if some_condition:
return render_template("baz.html")
else:
super_cool_logic()
return render_template("foo.html", messages={"main":"Condition failed on page baz"})
I feel like I'm missing something though and there's a better way to achieve what you're trying to do (I'm not really sure what you're trying to do)
You can however maintain your code and simply pass the variables in it separated by a comma: if you're passing arguments, you should rather use render_template:
#app.route("/baz")
def do_baz():
if some_condition:
return render_template("baz.html")
else:
return render_template("/foo", messages={"main":"Condition failed on page baz"})
I'm trying to use the audio player Timeside in Django 1.5. JavaScript function that handles loading the player receives this set of parameters,
loadplayer.js
loadPlayer function (analizerUrl, soundUrl, soundImgSize, itemId, visualizers, CurrentUserName, isStaffOrSuperuser)
In my template is the following script that handles launch
{% If item.file %}
loadPlayer('{% url 'item-analyze-xml' item.public_id %}',
"{% url 'item-export' item.public_id,"mp3" %}", undefined, '{{item.id}}', visualizers,
CURRENT_USER_NAME, //undefined if !user.is_authenticated
true); //true because superuser
{% Endif%}
URLs are configured as follows
url(r'^archives/items/download/(?P<public_id>[A-Za-z0-9._-]+)\.(?P<extension>'
+ export_extensions + ')$',
item_view.item_export,
name="item-export"),
url(r'^archives/items/(?P<public_id>[A-Za-z0-9._-]+)/analyze/xml/$',
item_view.item_analyze_xml,
name="item-analyze-xml"),
This worked in Django 1.4, attempt to update for be used in version 1.5. The problem is that I can not send the parameter MP3 here
"{% url 'item-export' item.public_id,"mp3" %}",
the view item_export is like this:
def item_export(self, request, public_id, extension):
When the player loads I get the following error:
Could not parse the remainder: ',' from 'pista.public_id,'
Which is the correct syntax for this on Django 1.5?
Using the name and equal solved this step
{% url 'item-export' public_id=item.public_id extension="mp3" %}
Seems like it is working but I have a new error:
NoReverseMatch at /pista/1/
Reverse for 'item-analyze-xml' with arguments '()' and keyword arguments '{u'public_id': ''}' not found.
I do not want to answer my own question but I've been searching and trying a lot and I found the solution.
{% Url 'impromusic-item-export' item.id 'mp3'%}
The first quotes refer to the name of the url(everybody know), item.id can be variable depends on the object, but if I write it in quotes it will be a string of characters wich not accept any value beyond which we assign.
That's my conclusion after 5 hours of searching but I can be wrong :) hope u understand me