I tried to break this problem down into the simplest example. When the request is ajax, rendering the page with an updated context doesn't produce the expected result.
index.html:
<html>
<body>
{% if templateVariable %}
<h1>{{ templateVariable }}</h1>
{% endif %}
<button id="testBtn">TEST</button>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$(function() {
$('#testBtn').click(function(event) {
$.ajax({
type: "POST",
url: "./",
data: {
'x' : 'x',
'csrfmiddlewaretoken' : '{{ csrf_token }}'
}
});
});
});
});
</script>
</body>
</html>
views.py
def index(request):
context = {}
if 'x' in request.POST:
context['templateVariable'] = 'I was updated via ajax'
print('ajax ran')
return render(request, 'index.html', context)
context['templateVariable'] = 'I was not updated via ajax'
print('ajax was not run')
return render(request, 'index.html', context)
When I first load the page, 'ajax was not run' is printed, templateVariable is 'I was not updated via ajax', and the page renders with that in the h1 tag as expected.
When I click the testBtn, I expect the ajax request to trigger the if statement, update the context, and render the page with 'I was updated by ajax' in the h1 tag.
Instead, 'ajax ran' is printed but templateVariable remains 'I was not updated by ajax' when the page is rendered. 'ajax was not run' only is printed once when the page is loaded initially.
Why would I not be getting the expected result?
EDIT: It seems everyone agrees that you cannot return a render and update context variables with an ajax request but I'm still having trouble with this as I believe this is possible. Here's some alternate code:
index2.html:
<html>
<body>
{% if templateVariable %}
<h1>{{ templateVariable }}</h1>
{% endif %}
<h1 id="placeHolder"></h1>
<button id="testBtn">TEST</button>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$(function() {
$('#testBtn').click(function(event) {
$.ajax({
type: "POST",
url: "./",
data: {
'x' : 'x',
'csrfmiddlewaretoken' : '{{ csrf_token }}'
},
success: function (data) {
$('#placeHolder').html(data);
},
dataType: 'html'
});
});
});
});
</script>
</body>
</html>
views2.py
def index2(request):
context = {}
if 'x' in request.POST:
context['templateVariable'] = 'I was updated via ajax'
print('ajax ran')
return render_to_response('notIndex.html', context)
context['templateVariable'] = 'I was not updated via ajax'
print('ajax was not run')
return render(request, 'index.html', context)
notIndex.html:
{% if templateVariable %}
{{ templateVariable }}
{% endif %}
In this example, the page is initially loaded with templateVariable in the context as 'I was not updated via ajax'.
When testBtn is clicked, the ajax request triggers the if block in the view. This renders notIndex.html with the updated context.
The success function of the ajax call sets the generated html from notIndex.html to the h1 tag.
So why is it only possible to trigger a page render with ajax if the page is not the same one that the ajax call came from?
You can't return renders or redirects from AJAX, that's how it works
If you want to update your UI based on something that happens on the server say you have a cart and you'd like to implement 'add to cart' without refreshing the page, the 'add to cart' button must request an endpoint you provided in your urls.py & that url must return true if object is added, or false if it wasn't, but it won't update it on the UI, you need to manually change the cart items count with Javascript.
If you try to redirect or return a render to ajax, it will get the HTML or the redirect/render, nothing else.
if you want to redirect, you'll want to do that with JS but with no context variables from django.
see this ref
Related
I'm using a for loop in a template to create multiple forms with method="post" that work with Ajax. But only the form for the first element of items_list works fine, the rest do not work at all showing error 405 0 Method Not Allowed. I think they all should work the same way. And just wondering if this issue was caused by a for loop or something else.
cart_items.html:
<script>
$(document).ready(function () {
$("#remove").click(function (event) {
event.preventDefault();
$.ajax({
url: '{% url "cart:remove_from_cart" %}',
type: "POST",
dataType: 'json',
data: {bookID: $('#idInput').val()},
success: function (response_data) {
alert('works fine')
},
error: function (response_data) {
console.log('error occurred');
}
});
});
});
</script>
{% for book in items_list %}
<div class="items">
<p id="title"> {{ book.book.title }}, quantity: {{ book.quantity }} </p>
<form method="post">
{% csrf_token %}
<input id="idInput" value="{{ book.book.id }}" >
<button id="remove" type="submit"> Remove</button>
</form>
</div>
{% endfor %}
The code in the function body below is just for testing. Once the first form works, I guess the problem was not caused by the function view.
cart/views.py:
#csrf_exempt
def remove_books(request):
cart = Cart.objects.get(user=request.user)
if request.method == 'POST':
passed_id = request.POST['bookID']
secured_id = int(passed_id)
response_data = {
'quantity': secured_id
}
return JsonResponse(response_data)
<script>
$(document).ready(function () {
$(".remove").click(function (event) {
// event.preventDefault(); // don't think it should be required with button type=button
var book_id = $(this).parent().find('.idInput').val(); // find correct input box.
var csrf = $('input[name="csrfmiddlewaretoken"]').val(); // get csrf token in variable.
// there are multiple ways to get csrf token, I personally like this ^^, see https://docs.djangoproject.com/en/2.1/ref/csrf/#ajax for more
$.ajax({
url: '{% url "cart:remove_from_cart" %}',
type: "POST",
dataType: 'json',
data: {
bookID: book_id,
csrfmiddlewaretoken: csrf // add csrf token to post data
},
success: function (response_data) {
alert('works fine')
},
error: function (response_data) {
console.log('error occurred');
}
});
});
});
</script>
{% csrf_token %} <!-- It will render a hidden input field with csrf token in it. Keep it outside for loop but in html. No need to render exactly same element multiple times. -->
{% for book in items_list %}
<div class="items">
<p class="title"> {{ book.book.title }}, quantity: {{ book.quantity }} </p>
<form method="post">
<input class="idInput" value="{{ book.book.id }}" > <!-- use class not id -->
<button class="remove" type="button"> Remove</button> <!-- you can use button type=button to avoid form submit and hence avoid event.preventDefault(); in js -->
</form>
</div>
{% endfor %}
I am using Django 1.5 and Dojo 1.8. I am trying to get Dojo to submit a form back to a Django view when I click a button.
Here is my Django view:
def report(request, report_id, report_url=None, template='report_parameters.html'):
if request.method == 'POST':
form = ReportParametersForm(request.POST)
if form.is_valid():
report_params = form.save()
html = "Success!"
return HttpResponse(html)
else:
form = ReportParametersForm()
return render(request,template, {
'form': form,
'report_url': report_url,
'report_id': report_id,
})
Here is the html page:
<div id="report_body">
<form data-dojo-type="dijit/form/Form" id="parameters_form" data-dojo-id="parameters_form">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<p><button id="submit_parameters" dojoType="dijit.form.Button" type="submit">Submit</button></p>
</form>
</div>
<script type="dojo/on" data-dojo-event="submit" data-dojo-args="e">
e.preventDefault();
require(["dojo/dom", "dojo/request", "dojo/dom-form"], function(dom, request, domForm){
on(dom.byId("submit_parameters"), "click", function() {
console.log("Dojo Post");
request.xhr("/report_parameters/report_id/report_url/", {
method: "post",
handleAs: "json",
data: domForm.toJson("parameters_form"),
}).then(
function(response){
alert(response);
dom.byId("report_body").innerHTML = "Report!";
},
function(error){
dom.byId("report_body").innerHTML = "<div class=\"error\">"+error+"<div>";
}
);
});
});
</script>
When I click the Submit button, I want to send a POST request to the url passing the data I have in my form. However, right now when I click Submit, the page reloads with a url looking something like this: /?csrfmiddlewaretoken=Y9gaNMFRWZNXMbJ2L3Ev7A5iKPGTuWeF¶m_1=0¶m2=0/report_parameters/report_id/report_url/.
I don't see the Dojo Post that should be appearing in my console.
How do I get my form to submit?
This fiddle seems to do what you want.
The major differences seem to be:
The <form> is actually a <div>. The Dojo documentation for Form links to reasons why this is done for IE.
All the related event script is inside the form <div>.
Remove the on(dom.byId("submit_parameters")... code, as there's already a declarative submit event handler.
HTML code:
<div id="report_body"></div>
<div data-dojo-type="dijit/form/Form" id="parameters_form" data-dojo-id="parameters_form" encType="multipart/form-data" action="" method="">
<input name="dummy" value="dummy">
<script type="dojo/on" data-dojo-event="submit" data-dojo-args="e">
console.log("submit");
e.preventDefault();
require(["dojo/dom", "dojo/request/xhr", "dojo/dom-form"], function(dom, xhr, domForm) {
console.log("Dojo Post");
var url = "/report_parameters/report_id/report_url/";
var data = domForm.toJson("parameters_form");
// overwrite url and data for jsfiddle
url = "/echo/json/";
data = {
json: data
};
xhr(url, {
method: "post",
handleAs: "json",
data: data,
}).then(function(response) {
alert(JSON.stringify(response, null, 2));
dom.byId("report_body").innerHTML = "Report!";
}, function(error) {
dom.byId("report_body").innerHTML = "<div class=\"error\">" + error + "<div>";
});
});
</script>
<button data-dojo-type="dijit/form/Button" id="submit_button" type="submit" name="submitButton" value="Submit">Submit</button>
</div>
JS code:
require(["dojo/parser", "dijit/registry", "dijit/form/Form", "dijit/form/Button", "dijit/form/ValidationTextBox", "dijit/form/DateTextBox", "dojo/domReady!"], function (parser, registry) {
parser.parse().then(function () {
console.log("parsed");
console.log(registry.byId("parameters_form"));
console.log(registry.byId("submit_button"));
});
});
I had to modify the above slightly. This is what eventually worked for me:
<div id="report_body"></div>
<form data-dojo-type="dijit/form/Form" id="parameters_form" data-dojo-id="parameters_form" encType="multipart/form-data" action="" method="POST">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<script type="dojo/on" data-dojo-event="submit" data-dojo-args="e">
e.preventDefault();
require(["dojo/dom", "dojo/request/xhr", "dojo/dom-form"], function(dom, xhr, domForm){
var url = "/report_parameters/report_id/report_url/"
var data = domForm.toObject("parameters_form")
xhr(url, {
method: "post",
data: data,
}).then(
function(response){
alert(response);
dom.byId("report_body").innerHTML = "Report!";
},
function(error){
dom.byId("report_body").innerHTML = error;
}
);
});
</script>
<p><button id="submit_parameters" dojoType="dijit/form/Button" type="submit" name="submitButton" value="Submit">Submit</button></p>
</form>
Using either the <div> or <form> tags to wrap the whole thing worked for me.
i have this Django application in which when user request for "localhost:8000/time/ ,it is shown html form input.html.
<head>
<!-- paste this in your footer -->
<script type="text/javascript">
$(document).ready(function() {
$('#myForm').submit(function() { // catch the form's submit event
$.ajax({ // create an AJAX call...
data: $(this).serialize(),
type: $(this).attr('POST'), // "post"
url: $(this).attr('/poi/'), // "/poi/"
success: function(response) {
// do something here
}
});
return false;
});
});
</script>
<!-- add jquery or it won't work -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
</head>
<body>
<form action="/poi/" method = "post">
<!---------
first name :<input type ="text" name ="fname">
last name:<input type ="text" name ="lname">
------>
enter a days :<input type ="text" name ="days">
<br>
<input type=submit name="submit" onclick="submit_function(); return false;">
</form>
</body>
when form is submitted user sees response.html page and URL is changed to "localhost:8000/poi/ is shown having
urls.py
urlpatterns = patterns('',
(r'^hello/$', hello),
('^time/$', current_datetime),
('^poi/$', next_datetime),
)
views.py
def current_datetime(request):
return render_to_response('input.html')
def next_datetime(request):
now = datetime.datetime.now()
now_day = now.day
now_month = now.month
now_year = now.year
return render_to_response('response.html', {'now_day': now_day, 'now_month'}}
now i have to do same thing but the url should not change from "localhost:8000/time/ to "localhost:8000/poi/
but it should be "localhost:8000/time/
how to accomplish that?
You have to use ajax to accomplish this
With jquery it would look something like
<form id="myForm"> ... </form> <!-- add an id to your form -->
<!-- paste this in your footer -->
<script type="text/javascript">
$(document).ready(function() {
$('#myForm').submit(function() { // catch the form's submit event
$.ajax({ // create an AJAX call...
data: $(this).serialize(),
type: $(this).attr('method'), // "post"
url: $(this).attr('action'), // "/poi/"
success: function(response) {
// do something here
}
});
return false;
});
});
</script>
<!-- add jquery or it won't work -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
and it should work out of the box
this is with an anonymous function, but you can put the code inside a named one and then add it to the button:
<input type="submit" onclick="submit_function(); return false;" />
it's basically the same
I recently started to learn jQuery and right now I am playing around with .ajax() function.
I cannot figure out how to access the get parameters in Django.
My code looks like:
Jquery & html:
<div id="browser">
<ul>
{% comment %}
Theres a script for each ctg. Each script fades out #astream, fades in #stream_loading and then it should display #astream with new values based on the GET param in ajax call
Prolly it wont work, but first I need to interact with the GET param in my views.py
{% endcomment %}
{% for ctg in ctgs %}
<script type="text/javascript" charset="utf-8">
(function($) {
$(document).ready(function() {
$("#stream_loading").hide()
$("#browse_{{ctg}}").click(function() {
$("#astream").fadeOut()
$("#stream_loading").fadeIn()
$.ajax({
type: "GET",
url: "/{{defo}}/?param={{ctg}}",
success: function() {
$("#stream_loading").fadeOut()
$("#astream").fadeIn()
}
});
});
});
})(jQuery);
</script>
<li><a id="browse_{{ctg}}" title="{{ctg}}">{{ctg}}</a></li>
{% endfor %}
</ul>
</div>
<div id="astream">
{{ajaxGet}} #just to see whats rendered
{% include "astream.html" %}
</div>
<div id="stream_loading">
loading stream, please wait ...
</div>
Django:
#https_off
def index(request, template='index.html'):
request.session.set_test_cookie()
path=request.META.get('PATH_INFO')
defo=path[1:path[1:].find('/')+1]
request.session['defo']=defo
defo=request.session['defo']
# build the stream sorted by -pub_date
import itertools
chained=itertools.chain(
model1.objects.order_by('-pub_date').filter(),
model2.objects.order_by('-pub_date').filter(),
)
stream=sorted(chained, key=lambda x: x.pub_date, reverse=True)
ajaxGet=request.GET.get('param','dummy')
if request.is_ajax():
template='astream.html'
ajaxGet=request.GET.get('param',False)
renderParams={'defo':defo, 'stream':stream, 'ajaxGet':ajaxGet}
return render_to_response(template, renderParams, context_instance=RequestContext(request))
Then I try to show it up in my template
{{ ajaxGet }}
But everytime is rendered as 'dummy'
In firebug I can see get requests with proper key and value.
What do I miss here?
Thanks
There is a frequent gotcha that people often fall into when doing this kind of Ajax, and that is not preventing the default action of the link/button. So your Ajax function never has a chance to fire, and the request that you're seeing in the Django code is caused by the normal page load - which is why is_ajax() is false.
Give the click handler a parameter, event, and call event.preventDefault(); at the end of the function.
I'm going to edit an ImageField using jquery ajax,so I've used jquery form plugin,this is the code:
<form id='form_upload' action="." method="POST" enctype="multipart/form-data">
<input type='file' id='id_HeadImage' name='id_HeadImage' />
</form>
<script typr="text/javascript">
var options = {
dataType: 'xml',
url: '{% url DrHub.views.editNews param1,param2 %}',
}
$('#form_upload').ajaxSubmit(options);
</script>
in <head>:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.js"></script>
<script src="http://malsup.github.com/jquery.form.js"></script>
and in server side :
if ('id_HeadImage' in request.FILES) and (request.FILES['id_HeadImage']):
gForm=GalleryForm(request.POST,request.FILES,instance=newsInstance.gallery_ptr)
if gForm.is_valid():
gForm.save()
as U can see I'm going to edit ImageField of a model named Gallery.
How can I do this?
this is Gallery Model:
class Gallery(models.Model):
HeadImage = models.ImageField(upload_to="gallery",blank=True,null=True)
While gForm.is_valid() returns True,but It won't be saved and Image of HeadImage Field won't be changed.
Note : I've checked this in firebug and I'm sure that data is sent and request.FILES has value.
what's wrong here?
EDIT : I've worked based on this article: http://www.laurentluce.com/posts/upload-to-django-with-progress-bar-using-ajax-and-jquery/
Try ajaxForm in place of ajaxSubmit:
`
<form id='form_upload' action="." method="POST" enctype="multipart/form-data">
<input type='file' id='id_HeadImage' name='id_HeadImage' />
</form>
<div id="empty">
</div>
<script type="text/javascript">
function handleResult(responseText, statusText, xhr, $form) {
//do stuff here
};
jQuery(document).ready(function() {
var options = {
target: '#empty',
// Not sure if you should use xml here, I would suggest json . ,
dataType: 'xml',
url: '{% url DrHub.views.editNews param1,param2 %}',
success: handleResult,
}
$('#form_upload').ajaxForm(options);
});
</script>`