I need to distinguish among multiple AJAX functions in a django view's POST method, to handle multiple forms.
The background:
I'd previously written this view without AJAX and got it working just fine. Before Ajax, I was able to distinguish the POST method for each form by adding name="some_button_name", like so:
if request.method == 'POST' and 'some_button_name' in request.POST: #check which form button submitted
This was great, but AJAX can make it better if I get it working. Now I'm not sure how to distinguish the ajax functions on the view side.
Here's how I think it should work (theoretical view):
if request.method == 'POST' and request.POST['some identifier_A from ajax function here']:
# do stuff
# save form_A
if request.method == 'POST' and request.POST['some identifier_B from ajax function here']:
# do stuff
# save form_B
if request.method == 'POST' and request.POST['some identifier_C from ajax function here']:
# do stuff
# save form_C
... but I'm stumped. Below is my (simplified but structurally accurate) code. It will of course want to call every model's save method regardless of which form/ajax function was called, so saving form C will screw up form B since the B's ajax handler didn't do anything or pass any JSON.
I've looked through the list of ajax parameters and it doesn't appear that I can use one to add some identifying variable to catch on the view side in POST... or is there?
If someone could shed some light a way to solve this I'd be hugely appreciative. Also, I'm not sure if it's relevant to this question, but I also want to eventually add a "save all" button that fires all the ajax functions.
Views.py
def update_view(request, slug):
mymodel = Model.objects.get(slug=slug)
form_A = Model_A_Form(instance=mymodel.model_a)
form_B = Model_B_Form(instance=mymodel.model_b)
form_C = Model_C_Form(instance=mymodel.model_c)
if request.method == 'POST': # using request.is_ajax(): here causes the same problem
form_A = Model_A_Form(request.POST, instance=mymodel.model_a)
if form_A.is_valid():
form_A.save()
return JsonResponse
form_B = Model_B_Form(request.POST, instance=mymodel.model_b)
if form_B.is_valid():
form_B.save()
return JsonResponse
form_C = Model_C_Form(request.POST, instance=mymodel.model_c)
if form_C.is_valid():
form_C.save()
return JsonResponse
context = {
'form_A': form_A,
'form_B': form_B,
'form_C': form_C,
'obj': mymodel,
}
return render(request, "products/update_form.html", context)
.JS
<script>
$(() => {
// Form A handler
$(function () {
$('#mybtn-a').click(function () {
var formA = $(".form-a-ajax")
var formAMethod = formA.attr("method");
var formAEndpoint = formA.attr("action");
formA.submit(function (event) {
event.preventDefault();
var formAData = formA.serialize()
var thisForm = $(this)
$.ajax({
method: formAMethod,
url: formAEndpoint,
data: formAData,
success: function (data) {
$.alter({
title: "Success!",
})
},
error: function (error) {
}
}) //end ajax
});//end click
})
})
// Form B handler
$(function () {
$('#mybtn-b').click(function () {
var formB = $(".form-b-ajax")
var formBMethod = formB.attr("method");
var formBEndpoint = formB.attr("action")
formB.submit(function (event) {
event.preventDefault();
var formBData = formB.serialize()
var thisForm = $(this)
$.ajax({
method: formBMethod,
url: formBEndpoint,
data: formBData,
success: function (data) {
$.alter({
title: "Success!",
})
},
error: function (error) {
}
}) // end ajax
});//end click
})
})
// Form C handler
$(function () {
$('#mybtn-c').click(function () {
var formC = $(".form-c-ajax")
var formCMethod = formC.attr("method");
var formCEndpoint = formC.attr("action")
formC.submit(function (event) {
event.preventDefault();
var formCData = formC.serialize()
var thisForm = $(this)
$.ajax({
method: formCMethod,
url: formCEndpoint,
data: formCData,
success: function (data) {
$.alter({
title: "Success!",
})
},
error: function (error) {
}
}) //end ajax
});//end click
})
})
Template:
<form method="post" action="{{ obj.get_my_url }}" class="form-a-ajax">
{% csrf_token %} {{ form_A }}
<button type='submit' id="mybtn-a" name='save_form_a' class='btn btn-success'>Save</button>
</form>
<form method="post" action="{{ obj.get_my_url }}" class="form-b-ajax">
{% csrf_token %} {{ form_B }}
<button type='submit' id="mybtn-b" name='save_form_b' class='btn btn-success'>Save</button>
</form>
<form method="post" action="{{ obj.get_my_url }}" class="form-c-ajax">
{% csrf_token %} {{ form_C }}
<button type='submit' id="mybtn-c" name='save_form_c' class='btn btn-success'>Save</button>
</form>
Thanks for your feedback everybody! Your comments gave me an idea. I have a working solution and it seems totally simple after the fact. No need to append anything to the JSON in my case. Every model I have has some unique field names; I just need to check for the presence of one of them in the JSON object of key:value pairs. This works:
if request.method == 'POST' and 'some_unique_model_field_name' in request.POST.keys():
form_A = Model_A_Form(request.POST, instance=mymodel.model_a)
if form_A.is_valid():
form_A.save()
return JsonResponse
#rinse and repeat...
Related
I am developing a web application through Django and I want to get information from my javascript to a view of Django in order to access to the database.
I am using an ajax call as this post shows.
I am calling the js in html by an onclick event :
sortedTracks.html
...
<form action="{% url 'modelReco:sortVideo' video.id %}">
<input type="submit" value="Validate" onclick="ajaxPost()" />
</form>
...
clickDetection.js
//defined here
var tracksSelected = [];
//function that fill tracksSelected
function tagTrack(track_num){
if(tracksSelected.includes(track_num)){
var index = tracksSelected.indexOf(track_num);
tracksSelected.splice(index, 1);
}else{
tracksSelected.push(track_num);
}};
//ajax function
function ajaxPost(){
$.ajax({
method: 'POST',
url: '/modelReco/sortedTracks',
data: {'tracksSelected': tracksSelected},
success: function (data) {
//this gets called when server returns an OK response
alert("it worked! ");
},
error: function (data) {
alert("it didnt work");
}
});
};
So the information I want to transfer is tracksSelected and is an array of int like [21,150,80]
views.py
def sortedTracks(request):
if request.is_ajax():
#do something
print(request)
request_data = request.POST
print(request_data)
return HttpResponse("OK")
The ajax post works well but the answer I get is only an empty Query Dict like this :
<QueryDict: {}>
And if I print the request I get :
<WSGIRequest: GET '/modelReco/sortedTracks/?tracksSelected%5B%5D=25&tracksSelected%5B%5D=27&tracksSelected%5B%5D=29'>
I have also tried to change to request_data=request.GET but I get a weird result where data is now in tracksSelected[]
I've tried to know why if I was doing request_data=request.GET, I get the data like this tracksSelected[] and get only the last element of it.
And I found a way to avoid to have an array in my data (tracksSelected) on this link
This enables me to have :
in views.py
def sortedTracks(request):
if request.is_ajax():
#do something
print(request)
request_data = request.GET.getlist("tracksSelected")[0].split(",")
print(request_data)
and in clickDetection.js
function ajaxPost(){
tracksSelected = tracksSelected.join();
$.ajax({
method: 'POST',
url: '/modelReco/sortedTracks',
data: {'tracksSelected': tracksSelected},
success: function (data) {
//this gets called when server returns an OK response
alert("it worked! ");
},
error: function (data) {
alert("it didnt work");
}
});
};
This little trick works and I am able to get the array data like this,
print(request_data) returns my array such as [21,25,27]
Thank you for helping me !
According to me to access the data which is sent in the ajax request can be directly accessed .
For Example:
def sortedTracks(request):
if request.method == 'POST':
usersV = request.POST.get('tracksSelected')[0]
for users in usersV:
print users
return HttpResponse("Success")
else:
return HttpResponse("Error")
The correct syntax is data: {tracksSelected: tracksSelected},
hello I want to delete a Django object using AJAX or JavaScript with confirm message on clicking delete but I don't know how to complete AJAX request.
views.py
def delete(request,id):
try:
inta = work.objects.get(pk=id)
inta.delete()
except:
pass
return HttpResponseRedirect(reverse('home'))
urls.py
url(r'^delete/(?P<id>\d+)/$',views.delete, name='delete')
html :
{& for app in apps &}
<p>{{ app.item0 }}</p>
<p>{{ app.item1 }}</p>
<p>{{ app.item2 }}</p>
<button data-object-id="{{ app.id }}">remove</button>
{% endfor %}
$('button').on('click', function(){
var confirmation = confirm("are you sure you want to remove the item?");
if (confirmation) {
// execute ajax
alert('removed');
}
})
There is more convinient way to write AJAX code:
$('button').on('click', function(){
let confirmation = confirm("are you sure you want to remove the item?");
if (confirmation) {
let object_id = $(this).attr('data-object-id');
let url = `delete/${object_id}/`;
$.ajax({
url: "example.html/my/example",
data: {
'csrfmiddlewaretoken': "{{ csrf_token }}"
},
type: "DELETE",
dataType: "json"
}).done(
function(){alert("Deleted");}
).fail(
function(){alert("Error");}
)
});
}
})
Assuming that
The delete view excepts a DELETE request
The javascript code is contained in the HTML
$('button').on('click', function(){
let confirmation = confirm("are you sure you want to remove the item?");
if (confirmation) {
let object_id = $(this).attr('data-object-id');
let url = `delete/${object_id}/`;
$.delete(url, {csrfmiddlewaretoken: '{{ csrf_token }}'}, function(){
alert('removed');
})
}
})
If the javascript code is in a separate file you will have to find another way to get the csrf-token, perhaps from a hidden input somewhere on the page.
I am using jquery elements that a user can drag and drop. I post the order of the elements to django using ajax.
Inside the django view I am able to work with the data that is posted from ajax.
Django views:
#this is the view where the jquery elements are being ordered by the user
def inside_exam(request):
if request.method=='POST':
form = MyForm(request.POST)
if form.is_valid():
#here I am able to retrieve the data from ajax and save it to a django model, code not shown here
return redirect('exam_results')
#the view redirected to from the inside_exam view
def exam_results(request):
#here I set the score for the exam and set the context, code not shown here
print(“all is set”)
return render(request, 'quizresults.html', context)
The print(“all is set”) is executed and I am able to print the html for quizresults.html in the browser. No errors are in the terminal window and this is shown in the terminal: "GET /exam_results/ HTTP/1.1" 200 8981.
But the same template is still shown, it is not showing the quizresults.html template. Any idea why the render(request, 'quizresults.html', context) is not working as expected?
By the way: when I use a django form without the jquery, everything works fine and the quizresults.html template is shown.
Since I want to show the user another template, but not update the current template, is ajax maybe not the correct way to send the jquery data in this case? If not, what would be a better way?
Edit, ajax code:
function dataToSend() {
{% load static %}
var node2 = document.getElementById('sortable');
var idsInOrder = $("#sortable").sortable('toArray');
console.log("the ids");
console.log(idsInOrder);
var fd = new FormData();
for(var i=0; i<idsInOrder.length; i++) {
j = i+1
fd.append('a'+j, idsInOrder[i]);
}
$.ajax({
type: 'POST',
data: fd,
cache: false,
processData: false,
contentType: false
}).done(function(data) {
//The data from the quizresults.html template is printed out here, but that template is not shown, the template in the browser is still the insidequiz.html template.
console.log("the data");
console.log(data);
});
}
window.onload = function init() {
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
};
Using redirect shortcut method in Django will return a HttpResponseRedirect object back to the AJAX, which it will be processed as a 302 Found status code, which then will make another request to the redirected resource and get the content. This does not seem to be the right way to do it, even though you get the content.
You can use the method exam_results to do the other work and return the required context, which shall be used to return the HttpResponse object using render method.
Then, with the data you get, you can replace the document with the template you receive.
Solution:
# views
#this is the view where the jquery elements are being ordered by the user
def inside_exam(request):
if request.method=='POST':
form = MyForm(request.POST)
if form.is_valid():
#here I am able to retrieve the data from ajax and save it to a django model, code not shown here
context = exam_results(request)
return render(request, 'quizresults.html', context)
# method to set the score for the exam
# return context from this method
def exam_results(request):
#here I set the score for the exam and set the context, code not shown here
# build context
return context
# javascript
$.ajax({
type: 'POST',
data: fd,
cache: false,
processData: false,
contentType: false
}).done(function(data) {
//The data from the quizresults.html template is printed out here, but that template is not shown, the template in the browser is still the insidequiz.html template.
console.log("the data");
console.log(data);
// replace the page with the new template
var newDoc = document.open("text/html", "replace");
newDoc.write(data);
newDoc.close();
// update the url
window.history.pushState('', 'title', "newurl");
});
Ref: History API MDN
I figured that ajax makes things complicated when it comes to redirecting. What I ended up doing was to create an HTML form (that is hidden) and then post that form to a url using javascript. No need for ajax.
HTML code:
<form id="form1" action='{% url 'inside_exam' %}' method="post" style="display:none;">
{% csrf_token %}
</form>
<p> <button type='submit' style="visibility" class="button button-long button-primary" onclick="sendData(this);">Send</button></p>
javascript code:
function sendData() {
var idsInOrder = $("#sortable").sortable('toArray');
var form = document.getElementById('form1');
for(var i=0; i<idsInOrder.length; i++) {
j = i+1
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", 'a'+j);
hiddenField.setAttribute("value", idsInOrder[i]);
form.appendChild(hiddenField);
}
console.log("form is:");
console.log(form);
form.submit();
}
I have this url:
(r'^oyla/(\d+)/$', oyla),
I have this view:
#login_required
def oyla(request, id):
if request.is_ajax():
entry = Entry.objects.get(pk=id)
entry.points += 1
entry.save()
if 'HTTP_REFERER' in request.META:
return HttpResponseRedirect(request.META['HTTP_REFERER'])
return HttpResponseRedirect('/')
I have these in html:
{{ ent.points }}
Vote Up
and this is my js:
<script type="text/javascript">
$("a.oyla").click(function(){
var curr_elem = $(this) ;
$.get($(this).attr('href'), function(data){
var my_div = $(curr_elem).parent().find("b");
my_div.text(my_div.text()*1+1);
});
return false;
});
</script>
I want to run the vote up when I click "Vote Up" I want to see the points are increasing without any page refresh. It does not works. It says
The view baslik.views.oyla didn't return an HttpResponse object.
What can I do to fix this?
Thanks.
Try using $.ajax with cache disabled. I've read that using $.ajax works with all browsers but $.get won't send the ajax header with IE. In that case is_ajax() isn't True and no HttpResponse object is returned. Use:
$("a.oyla").click(function(){
var $curr_elem = $(this);
$.ajax({
url: $curr_elem.attr('href'),
cache: false,
success: function (data) {
$curr_elem.parent().find("b").text(my_div.text()*1+1);
}
});
return false;
});
Im trying to make a page that searched for users based on a users criteria. It then loads the results via AJAX response into a different DIV:
function search_friends() {
$("#search-results").show();
$("#loading1").show();
var q = $("#id_q").val();
var type = $("#id_search_type").val();
$("#loading1").hide();
$("#results").load("/search/friends/?ajax&q="+encodeURIComponent(q)+"&search_type="+encodeURIComponent(type));
return false;
}
Inside this new results DIV, I have links for each user to be able to add them as a friend. Each link in this results DIV has a ID of each users userID and a class of user_link. When the logged in user clicks on the link I want it to pop up a confirmation box then send the request via AJAX. However, I cannot get the links to submit via AJAX like I want. The code I have is below:
{% for u in users %}
<div id="results">
<img src="{{ u.profile_pic }}" class="xsmall-pic" /> {{ u.username }}<br />
<span class="small-date">{{ u.get_full_name }}</span>
<span class="floatR" id="user_{{ u.id }}_link">Add as friend</span>
</div>{% endfor %}
<script type="text/javascript" language="javscript">
$(document).ready(function() {
var csrf_token = "{{ csrf_token }}";
$(".user_link").bind('click',function() {
request_friend($(this).id,csrf_token)
});
$("#search-friends-form").submit(search_friends);
});
</script>
In an external JavaScript file I have the following:
function confirm_request()
return confirm("Are you sure you want to request this user as a friend?");
}
function request_friend(id,token)
if (confirm_request())
{
var data1 = {to_friend: id,csrfmiddlewaretoken: token};
$.post('/users/requests/friends/'+id+'/?ajax',
data1, function(json) {
$("#user_"+id+"_link").html(json.status);
}, "json");
return false;
}
else
{
return;
}
}
Thanks for any help as Im not all that great with Javascript.
EDIT Python function called via AJAX
def search_for_friends(request):
users = False
friends = False
return_page = 'users/friend_search.html'
ajax = 'ajax' in request.GET
try:
if 'q' in request.GET:
form = FriendSearchForm(request.GET)
if form.is_valid():
if form.cleaned_data['search_type'] == 'username':
users = CustomUser.objects.exclude(pk=request.user.id).filter(username__icontains=form.cleaned_data['q'])
elif form.cleaned_data['search_type'] == 'name':
users = CustomUser.objects.filter(Q(first_name__icontains=form.cleaned_data['q']) | Q(last_name__icontains=form.cleaned_data['q']))
elif form.cleaned_data['search_type'] == "email":
users = CustomUser.objects.filter(email__icontains=form.cleaned_data['q'])
else:
pass
else:
pass
else:
form = FriendSearchForm()
if users != False:
users = users
error = False
if users == "":
users = ""
error = "No users match the search term provided"
if ajax:
show_results = True
context = RequestContext(request,{'users':users,'form':form,'show_results':show_results})
return render_to_response('users/friend_search_results.html',context_instance=context)
context = RequestContext(request,{'users':users,'form':form,'error':error})
return render_to_response(return_page,context_instance=context)
except:
form = FriendSearchForm()
context = RequestContext(request,{'form':form})
return render_to_response(return_page,context_instance=context)
If you want to keep from having the JavaScript in the AJAX response, you could have the completion event of .load() take the responsibility of setting up the click events:
var url = "/search/friends/?ajax&q="+encodeURIComponent(q)+"&search_type="+encodeURIComponent(type);
$("#results").load(url, function() {
var csrf_token = "{{ csrf_token }}";
$(".user_link").bind('click',function() {
request_friend($(this).id,csrf_token)
});
});
if I understand your question correctly, you want to make sure click handlers work for links loaded through a later AJAX call?
In jQuery, use the $('.user_link').live('click', function() {}) handler once to assign a click event handler to all current and future links.
The Javascript being returned from your AJAX call does not get evaluated unless you specifically tell jQuery to evaluate it. This is done by specifying the dataType on your AJAX calls, which .load() doesn't support.
As seen in this answer, you should use jQuery.get() or $.ajax and specify your data type:
$.ajax({
type: "GET",
url: "yourPage.htm",
dataType: "html"
});