Getting checkbox value in flask - flask

I'm trying to poll the state of checkboxes (this is done in JS every three seconds). This solution returns "None" (see code below); Both printouts (with 'args' and with 'form') return "None". I'm expecting True/False, depending on the checkbox's boolean state.
index.html:
{% extends "layout.html" %}
{% block content %}
<div id="results" class="container">{{data_load|safe}}</div>
<input id='testName' type='checkbox' value='Yes' name='testName'>
{% endblock %}
and the relevant flask app snippet:
#app.route('/', methods = ['GET', 'POST'])
def index():
return render_template('index.html', data_load=timertry())
#app.route('/_timertry', methods = ['GET', 'POST'])
def timertry():
print request.args.get('testName')
print request.form.get('testName')
return "some html going into 'results' div.."
The JavaScript polling function (adapted from here):
$(document).ready(function() {
$.ajaxSetup({cache : false});
setInterval(function() {
$('#results').load('/_timertry?' + document.location );
}, 3000); // milliseconds!
});
This should be simple enough, but none of the SO solutions I looked into (e.g., using jquery, adapting the flask/ajax example, etc.) worked.
EDIT: following mark's suggestion (including the javascript) and adding
print request.values in index.html returns (seen on the console in Aptana):
CombinedMultiDict([ImmutableMultiDict([]), ImmutableMultiDict([])])
Clearly, the request seems empty. The post request is logged (when checkbox is pressed) as:
127.0.0.1 - - [03/Oct/2013 00:11:44] "POST /index HTTP/1.1" 200 -
Any ideas here?

Your javascript does the following every three seconds...
$('#results').load('/_timertry?' + document.location);
In other words, you are using the jQuery load() function. The documentation for that function states that this will, in essence, call an HTTP get request to the URL you provide as the parameter (something like /_timertry?http://www.example.org. In other words, a GET request will be called to /timertry?http://www.example.org, which will be handled by Flask's timertry method.
When you have an HTTP form, and you click the "Submit" button, the browser does some magic to push all of the values to the server in the request. When you just do a simple AJAX request, none of that happens for you. Instead, you need to explicitly state what you want to be passed as data to the server (although there are plugins to help you with "post the values of an HTML form using AJAX").
So, because at no point did you do anything in your Javascript to retrieve the value of checkbox to include it into the AJAX request, the AJAX request has no values specifying that the checkbox was checked. You would need to have jQuery check if the box is checked...
var isChecked = $('#testName').is(':checked');
# Now do something with isChecked...
From what I can tell, however, you are sort of misusing HTTP: the load function will make a GET request, and you probably want something to happen as a request of the request. You probably want to make it do a POST request instead (see here and here). Also, you mentioned that you're looking for something to post when a value is changed. Putting this together, you can do something like this...
// Ensure that the function is called when the DOM is ready...
$(function() {
// Register an event that should be fired when the checkbox value is changed
$('#testName').change(function() {
var data = { isChecked : $(this).is(':checked') };
$.post('/', data);
});
})
In this case, we have an event that is called when a checkbox is checked, and that event causes us to make a POST request to the server.

I'm going to answer this question which was found in the comments of the question
"which becomes a question of how to submit a form without a 'submit' button.."
So it is very possible to submit a value when a user clicks on the button
{% block content %}
<form id="target" action="YourViewName">
<div id="results" class="container">{{ data_load|safe }}</div>
<input id='testName' type='checkbox' value='Yes' name='testName'>
</form>
{% endblock %}
$( "#results" ).click(function() {
$( "#target" ).submit();
});
If you want to stay on the same page, however, you're going to need to use an ajax call to pass the data back rather then use a standard submit, however This tutorial covers that topic fairly well. but a basic change to send the data back would look like
$( "#results" ).click(function() {
var request = $.ajax({
type: "POST",
url: "/YourViewName",
data: {'input_value':$('#testName').val()},
dataType: "html"
}).done(function(msg) {
// I don''t know what you want to do with a return value...
// or if you even want a return value
}
});
and the flask would look like
#app.route("/YourViewName")
def example():
list_name = request.args.get("input_value")
#if you don't want to change the web page just give a blank return
return ""

Related

When {% csrf_token %} may be inconvenient?

Django 1.10
In the documentation we can read:
While the above method can be used for AJAX POST requests, it has some
inconveniences: you have to remember to pass the CSRF token in as POST
data with every POST request.
https://docs.djangoproject.com/es/1.10/ref/csrf/#ajax
The mentioned "above method" is about adding {% csrf_token %} to forms.
And it is said in the documentation that more convenient may be:
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
Well, I can't catch in which case {% csrf_token %} may be inconvenient.
I tried:
<form action="" method="post">{% csrf_token %}
function post_create(){
$.ajax({
method: "POST",
url: '{{ object.get_frame_date_create_url }}',
data: $("#object_form").serialize(),
success: add_post_data,
error: fail,
});
}
Seems to be working. The result of $("#object_form").serialize() looks like this:
"csrfmiddlewaretoken=NFgXO1gsHJbi0N1IUb5ZPQ2rno2RGBrRR8kxboewWDC63sm2hxlvXtUtyviCSoZ1&date=2015-01-01&precision=F&frame=1"
What must I remember here? How can I forget to pass csrfmiddlewaretoken? It is just an ordinary field of the form. Hidden from users, but not from the programmer.
Is it supposed that there are cases when it is necessary to manually touch every field in the form and send it separately. I can't imagine that.
Your comment would be highly appreciated: do I understand correctly that $("#object_form").serialize() is fine? And in which case it is more convent to use ajaxSetup?
There's absolutely no reason why you need an HTML form to send a POST request. For example, both the upvote/downvote buttons and the favorite button on this page are simple buttons that send an AJAX POST request without ever touching an HTML form.
Submitting a form asynchronously is just one of the many use-cases for AJAX. If that is the only way you use AJAX, the code from the docs may not give you much of an advantage. However, if you use it for one the many other use-cases, it will be much more convenient.

Flask - how to get query string parameters into the route parameters

Im very much new to Flask, and one of the starting requirements is that i need SEO friendly urls.
I have a route, say
#app.route('/sales/')
#app.route(/sales/<address>)
def get_sales(addr):
# do some magic here
# render template of sales
and a simple GET form that submits an address.
<form action={{ url_for('get_sales') }}>
<input type='text' name='address'>
<input type=submit>
</form>
On form submission, the request goes to /sales/?address=somevalue and not to the standard route. What options do I have to have that form submit to /sales/somevalue ?
I feel like I'm missing something very basic.
You would need to use JavaScript to achieve this so your template would become:
<input type='text' id='address'>
<button onclick="sendUrl();">submit</button>
<script>
function sendUrl(){
window.location.assign("/sales/"+document.getElementById("address").value);
}
</script>
and your routes similar to before:
#app.route('/sales/')
#app.route('/sales/<address>')
def get_sales(address="Nowhere"):
# do some magic here
# render template of sales
return "The address is "+address
However, this is not the best way of doing this kind of thing. An alternative approach is to have flask serve data and use a single-page-application framework in javascript to deal with the routes from a user interface perspective.
There is a difference between the request made when the form is submitted and the response returned. Leave the query string as is, as that is the normal way to interact with a form. When you get a query, process it then redirect to the url you want to display to the user.
#app.route('/sales')
#app.route('/sales/<address>')
def sales(address=None):
if 'address' in request.args:
# process the address
return redirect(url_for('sales', address=address_url_value)
# address wasn't submitted, show form and address details
I'm not sure there's a way to access the query string like that. The route decorators only work on the base url (minus the query string)
If you want the address in your route handler then you can access it like this:
request.args.get('address', None)
and your route handler will look more like:
#pp.route('/sales')
def get_sales():
address = request.args.get('address', None)
But if I were to add my 2 cents, you may want to use POST as the method for your form posting. It makes it easier to semantically separate getting data from the Web server (GET) and sending data to the webserver (POST) :)

form submit jQuery mobile

I've gotten it into my head that mobile applications don't like form submits the same way html does, so I thought I'd better have a sanity check on Stackoverflow.
For example, instead of having <input type="submit"...>, it looks like I should now use <a data-role="button"...>
Q: Can I continue to use <input type="submit"...> for mobile applications?
The reason why I ask is because the action page has some logic, such as:
<cfif structKeyExists(form,"Save")>
jQuery Mobile, at least as of this writing, by default submits forms via AJAX using the method specified on the form being submitted. POST submissions will still be posted to the server in the background, so ColdFusion will still see the form variables that are passed in as usual. When a response is generated, jQuery Mobile will take the response and transition the view over to whatever HTML was returned. In my own testing you can continue to use a normal submit button as well. If you want a standard submission rather than an AJAX submission, add data-ajax="false" to the form tag.
If you want to programatically submit a form, set the data-ajax attribute for the form to false and then set an event handler for the submit event for the form:
<form data-ajax=false></form>
$(function () {
$('form').bind('submit', function (event) {
event.preventDefault();
$.post('path/to/server.file', $(this).serialize(), function (data) {
alert('Server Response: ' + data);
});
});
});

jquery-autocomplete does not work with my django app

I have a problem with the jquery-autocomplete pluging and my django script. I want an easy to use autocomplete plugin. And for what I see this (http://code.google.com/p/jquery-autocomplete/) one seems very usefull and easy. For the django part I use this (http://code.google.com/p/django-ajax-selects/) I modified it a little, because the out put looked a little bit weired to me. It had 2 '\n' for each new line, and there was no Content-Length Header in the response. First I thought this could be the problem, because all the online examples I found had them. But that was not the problem.
I have a very small test.html with the following body:
<body>
<form action="" method="post">
<p><label for="id_tag_list">Tag list:</label>
<input id="id_tag_list" name="tag_list" maxlength="200" type="text" /> </p>
<input type="submit" value="Submit" />
</form>
</body>
And this is the JQuery call to add autocomplete to the input.
function formatItem_tag_list(row) {
return row[2]
}
function formatResult_tag_list(row) {
return row[1]
}
$(document).ready(function(){
$("input[id='id_tag_list']").autocomplete({
url:'http://gladis.org/ajax/tag',
formatItem: formatItem_tag_list,
formatResult: formatResult_tag_list,
dataType:'text'
});
});
When I'm typing something inside the Textfield Firefox (firebug) and Chromium-browser indicates that ther is an ajax call but with no response. If I just copy the line into my browser, I can see the the response. (this issue is solved, it was a safety feature from ajax not to get data from another domain)
For example when I am typing Bi in the textfield, the url "http://gladis.org/ajax/tag?q=Bi&max... is generated. When you enter this in your browser you get this response:
4|Bier|Bier
43|Kolumbien|Kolumbien
33|Namibia|Namibia
Now my ajax call get the correct response, but there is still no list showing up with all the possible entries. I tried also to format the output, but this doesn't work either. I set brakepoints to the function and realized that they won't be called at all.
Here is a link to my minimum HTML file http://gladis.org/media/input.html
Has anybody an idea what i did wrong. I also uploaded all the files as a small zip at http://gladis.org/media/example.zip.
Thank you for your help!
[Edit]
here is the urls conf:
(r'^ajax/(?P<channel>[a-z]+)$', 'ajax_select.views.ajax_lookup'),
and the ajax lookup channel configuration
AJAX_LOOKUP_CHANNELS = {
# the simplest case, pass a DICT with the model and field to search against :
'tag' : dict(model='htags.Tag', search_field='text'),
}
and the view:
def ajax_lookup(request,channel):
""" this view supplies results for both foreign keys and many to many fields """
# it should come in as GET unless global $.ajaxSetup({type:"POST"}) has been set
# in which case we'll support POST
if request.method == "GET":
# we could also insist on an ajax request
if 'q' not in request.GET:
return HttpResponse('')
query = request.GET['q']
else:
if 'q' not in request.POST:
return HttpResponse('') # suspicious
query = request.POST['q']
lookup_channel = get_lookup(channel)
if query:
instances = lookup_channel.get_query(query,request)
else:
instances = []
results = []
for item in instances:
results.append(u"%s|%s|%s" % (item.pk,lookup_channel.format_item(item),lookup_channel.format_result(item)))
ret_string = "\n".join(results)
resp = HttpResponse(ret_string,mimetype="text/html")
resp['Content-Length'] = len(ret_string)
return resp
You probably need a trailing slash at the end of the URL.
Also, your jQuery selector is wrong. You don't need quotes within the square brackets. However, that selector is better written like this anyway:
$("input#id_tag_list")
or just
$("#id_tag_list")
Separate answer because I've just thought of another possibility: is your static page being served from the same domain as the Ajax call (gladis.org)? If not, the same-domain policy will prevent Ajax from being loaded.
As an aside, assuming your document.ready is in your Django template, it would be a good idea to utilize the {% url %} tag rather than hardcoding your URL.
$(document).ready(function(){
$("input[id='id_tag_list']").autocomplete({
url:'{% url my_tag_lookup %}',
dataType:'text'
});
});
This way the JS snippet will be rendered with the computed URL and your code will remain portable.
I found a solution, but well I still don't know why the first approach didn't worked out. I just switched to a different library. I choose http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/. This one is actually promoted by jQuery and it works ;)

Render to response to a redirected url in Django

In a form submission scenario, form is post to "/submit". I want to redirect user to "/sucess" on success and pass render some message to a template at new url. How to do this in Django? render_to_response doesn't do redirect and HttpResponseRedirect doesn't do template rendering.
The response from Daniel Roseman is potentially dangerous, opening your site to XSS vulnerabilities.
Make sure you strip html tags from whatever message is passed if you must do it this way.
A better way would be to redirect to /success/?msg=ok, and have in your view a dictionary:
{ 'ok': 'Your success message' }
If your success page needs a dynamic message, you need to pass it there somehow. You can either do this via GET parameters in the URL you are redirecting to - eg return HttpResponseRedirect('/success/?msg=My+success+message+here') - or by setting some values in the session, which the success view can pick up.
The best way to do what you want is:
In your view set the success message (or error message) as a dict. And use the render_to_response to display any template.
In your template you can verify if there is a message and if the message is an error message or a success message.
Some like this:
In your view:
if anyError:
dict = {"message" : {"error" : "The error message"}}
else:
dict = {"message" : {"success" :"The success message"}}
in your template:
{% if message %}
{% if message.error %}
{{ message.error }}
{% else %}
{{ message.success }}
{% endif %}
{% endif %}
Hope i've helped
There is no good way to do what you want. The browser submits the form, which will take the user to /submit. Once you decide whether it's "successful", it's too late; the browser has already decided on it's destination. All you can do is redirect. But, as you're finding out, it's going to be hard to keep your POST state when you redirect.
I would give up on this. It's not how websites are supposed to work.
I think Django does this already if you extend the generic view. On create there's an optional argument "post_save_redirect". This functionality is used in the automatically generated Django admin system. For example if you want a new resource, say user, you go to the url "/user/add". After you submit it redirects you to "/user/1" with a success message "you just saved user #1". So this is possible, but I'm still researching how to do it without extending the generic view. I'm still very new to Django and python, so looking through Django core to find how they did it is not so easy for me.
I would recommend to use Django message framework which is used for one-time notification messages.
https://docs.djangoproject.com/en/1.8/ref/contrib/messages/