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();
}
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},
My site currently renders forms on their own page. I'm trying to get them to render inside a sidebar div tag now on my main page. However, I can't figure out how to shape the JavaScript and/or View so I get the HTML of the form template back and inserted into the div tag.
UPDATE
I'm getting the following error in the console: GET http://127.0.0.1:8000/new_trend/ 500 (Internal Server Error)
HTML (tag on the main page which I want to inject the form template into):
<div id="sidebar">
</div>
JavaScript
$(function() {
$("#new-trend").click(function(event){
alert("User wants to add new trend"); //this works
$.ajax({
type: "GET",
url:"/new_trend/",
success: function(data) {
$('#sidebar').html(data),
openNav()
}
})
});
});
VIEW
def new_indicator(request):
# if this is a POST request we need to process the form data
if request.method == "POST":
# create a form instance and populate it with data from the request:
form = IndicatorForm(request.POST)
# check whether it's valid:
if form.is_valid():
indicator = form.save(commit=False)
indicator.author = request.user
indicator.modified_date = timezone.now()
indicator.save()
return redirect('dashboard')
else:
form = IndicatorForm()
return render(request, 'mysite/sidebar_trend.html', {'form': form})
I was able to figure this out on my own. For others who come across this (myself included!), here's how I got it working.
JavaScript
There was a couple of fixes here. First you need to include the csrftoken, which yo can get through another JS function. Second, the AJAX request needs to be a POST, not a GET (not sure why, if you know please comment below). Here's the updated code snippet...
// Get cookie for CSRF token (from Django documentation)
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]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
};
// Load new Trend form
$(function() {
$("#new-trend").click(function(event){
var csrftoken = getCookie('csrftoken');
$.ajax({
type: "POST",
url: "/new_trend/",
data: {'csrfmiddlewaretoken': csrftoken},
success : function(data) {
$('#sidebar').html(data);
openNav()
}
})
alert("User wants to add new trend") //this works
});
});
VIEW
The second thing that needs to be corrected is the View function. First you need to render the HTML into a string, then return the string in an HttpResponse. This blog post provides a detailed explanation of why so I'm not going to go into it here. This is what the new code looks like...
#login_required
def ajax_indicator_form(request):
form = IndicatorForm()
html = render_to_string('mysite/sidebar_trend.html', {'form': form})
return HttpResponse(html)
I am able to send the value of the input field with the id datepicker to the server. And with this in the view I filter the time slot by the ones that occur on the same date.
Where I am having difficulties is sending this queryset to the browser and then appending this to the options.
I'm still relatively new to javascript I apologize if this is a newb question. I definitely appreciate any feedback!
My View:
if request.is_ajax():
selected_date = request.POST['selected_date']
slots_on_day = Calendar.objects.filter(date=selected_date)
return HttpResponse(slots_on_day)
My Javascript:
$(document).ready(function() {
$("#datepicker").change(function(){
document.getElementById("display_slots").style.display ="block";
$.ajax({
type: 'POST',
data: {
'selected_date':$('#datepicker').val(),
'csrfmiddlewaretoken' : $("input[name=csrfmiddlewaretoken]").val()
},
success: function(resp){
for (var i=0; i < resp['slots_on_day'].length;++i){
addOption(
document.getElementById("display_slots"), resp['slots_on_day'][i], resp['slots_on_day'][i]);
}
}
});
});
});
Notes: the id=datepicker field triggers the ajax event. I then want to receive the response from the server and append the options to the input with the id=display_slots
Error:
TypeError: resp.slots_on_day is undefined
Updated ajax success
success: function(data){
$.each(data, function(key, value){
$('select[name=display_slots]').append('<option value="' + key + '">' + value +'</option>');
});
}
from django.forms.models import model_to_dict
if request.is_ajax():
selected_date = request.POST['selected_date']
slots_on_day = Calendar.objects.filter(date=selected_date)
data = []
for cal in slots_on_day:
data.append(model_to_dict(cal))
return JsonResponse(status=200, data={'slots_on_day':data})
It'll send JSON response in frontend and now you can use this data as you want to use. I think, It'll work with your current ajax success method code
I am building a Django app that has a main dashboard with a bunch of on/off toggles. My goal would be to allow the user to toggle their settings on or off and have their changes automatically saved to the database.
So I followed this tutorial to leverage Ajax form submissions in my Django app so that the user would not have to reload the page. The problem that I am having (I think) is that the checkbox values are being POSTed to my Django views as 'on' whereas they should be POSTed as 'True'.
I think that this is the cause of the error, because I see this in my error logs:
Exception Type: IntegrityError at /update_usersettings/
Exception Value: dataplan_usersettings.normalize_person_name_proper may not be NULL
...
POST:
id_normalize_person_name_proper = u'on'
id_normalize_company_name_proper = u'on'
My existing JavaScript and Django views.py are here: https://gist.github.com/joefusaro/25b6221536291c1ba0d1
Update:
I've added the relevant Django template and forms code here. Not that I am using widget_tweaks for form rendering. The form renders like so:
<form action="/update_usersettings/" method="POST" id="usersettings_form">
<input checked="checked" class="bootstrap-switch-default" id="id_normalize_person_name_proper" name="bootstrap-switch" type="checkbox" />
<input checked="checked" class="bootstrap-switch-default" id="id_normalize_company_name_proper" name="bootstrap-switch" type="checkbox" />
<input type="submit" value="Post">
FINAL UPDATE
Thanks to Animesh for a great starting point. Here is the final Ajax code required. Special thanks to Victor K. for his help figuring this out!
Here is the final Ajax code:
$(function() {
// Submit post on submit
$('#usersettings_form').on('submit', function(event){
event.preventDefault();
console.log("Form submitted...") // sanity check
save_usersettings();
});
$('input[type="checkbox"]').on('switchChange.bootstrapSwitch', function(event, state) {
$('#usersettings_form').submit();
});
// AJAX for posting.
function get_post_data() {
var data = {};
$('input:checkbox').each(function () {
var $this = $(this);
var id = $this.attr('id');
data[id.replace('id_', '')] = $this.is(':checked');
});
return data;
}
function save_usersettings() {
console.log("Saving user settings...") // sanity check
$.ajax({
url : "update_usersettings/", // the endpoint
type : "POST", // http method
data : get_post_data(),
// handle a successful response
success : function(json) {
console.log(json); // log the returned json to the console
// $("#talk").prepend("<li><strong>"+json.text+"</strong> - <em> "+json.author+"</em> - <span> "+json.created+
// "</span> - <a id='delete-post-"+json.postpk+"'>delete me</a></li>");
console.log("Successfully saved user settings."); // another sanity check
},
// handle a non-successful response
error : function(xhr,errmsg,err) {
// $('#results').html("<div class='alert-box alert radius' data-alert>Oops! We have encountered an error: "+errmsg+
// " <a href='#' class='close'>×</a></div>"); // add the error to the dom
console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
}
});
};
And here is the final views.py:
class UpdateUserSettings(LoginRequiredMixin, UpdateView):
model = UserSettings
form = UserSettingsForm
def post(self, request, *args, **kwargs):
if request.method=='POST':
response_data = {}
form = UserSettingsForm({})
us = UserSettings.objects.get(user=1)
VALUE_MAP = {
'false': False,
'true': True
}
for name, field in form.fields.items():
if isinstance(field, BooleanField):
if request.POST.get(name):
if request.POST[name] in VALUE_MAP.keys():
setattr(
us,
name,
VALUE_MAP[request.POST[name]]
)
us.save()
response_data['result'] = 'Update successful!'
return HttpResponse(
json.dumps(response_data),
content_type="application/json"
)
else:
return HttpResponse(
json.dumps({"nothing to see": "this isn't happening"}),
content_type="application/json"
)
You can handle this inside the view.
CHECKBOX_MAPPING = {'on':True,
'off':False,}
class UpdateUserSettings(LoginRequiredMixin, View):
model = UserSettings
def post(self,request,*args,**kwargs):
normalize_person_name_proper = CHECKBOX_MAPPING.get(request.POST.get('normalize_person_name_proper'))
You can do this for all the fields you are supposed to receive in checkboxes from the user.
Also, one thing to note here is that you need not use request.method=='POST'check inside the post method of a class based generic view. The method will be called only when the request is POST.What you are looking for is if request.is_ajax():
Hope this helps.
I am new with Django and JavaScript and I am struggling trying to pass some values from a html table rendered with django-tables2
I want to open a django template passing 3 arguments.
I have an html table with a drop down button with several options.
every open a different django template. I get the values from the table with this JS function:
<script type="text/javascript">
function param(){
$(function(){
var http = new XMLHttpRequest();
var tbl = document.getElementById("myTable");
var rows = tbl.rows;
var url = "/recepcion/";
for (var i = 0; i < rows.length; i++){
rows[i].onclick = function(){
idpro = this.cells;
ano = this.cells;
mes1 = this.cells;
$.ajax({
type:"POST",
url: "/recepcion/",
datatype: "json",
data: ({idprov: idpro[0].innerHTML, anio: ano[1].innerHTML, mes: mes1[2].innerHTML }),
success: function(){
http.open("POST",url,true),
http.send(data)
}
});
}
}
});
}
</script>
The functionality that i need is: When I click in the first option of the drop down button call this function. The function will take the value from some cells and pass this values as arguments to another template where i can edit some record.
This is the view where I try to pass the arguments
views.py
def RecepcionView(request):
idp = request.POST.get('idpro')
year = request.POST.get('year')
month = request.POST.get('month')
if request.method == 'POST':
r = DetalleRecepcion.objects.get(id_proveedor=idp,year=year,month=month)
form = RecepcionForm(request.POST, instance=r)
if form.is_valid():
form.save()
return HttpResponseRedirect('/monitor/')
else:
r = DetalleRecepcion.objects.get(id_proveedor=idp,year=year,month=month)
form = RecepcionForm(instance=r)
return render_to_response('recepcion.html',
{'form':form},
context_instance=RequestContext(request))
UPDATE
I change the function and I put all inside a function and now can I pass the arguments, but I get the error:
ReferenceError: data is not defined
http.send(data)
In firebug I can see the function pass the value to the view and the view answer the form with the info that I need, but I need to open the form not only pass the value.
Any advice.