AJAX and DJANGO form - javascript

I'm a newbie to both Ajax and Django. To simplify the question assume I have one hidden form and two objects, and I ask the users to click some of the position of the objects. Each clicks fills out a part of the form. I want the form to be filled out once for each object. so twice overall. When the form is filled for the first subject I want to send the form via Ajax to the server, without refreshing the page, and allow the form to be refilled for the second object. However, for some reason I can't get the form to be send via ajax.
here is my code:
index.html:
<html>
<body>
<script src="{{ STATIC_URL }}prototype.js"></script>
<script src="{{ STATIC_URL }}jquery.js"></script>
<script>
objectID= 1;
num_obj = {{num_obj}}
function ajax_post(){
$('#inputform').submit(function() {
$.ajax({
type: 'POST',
url: '/index/',
data: $(this).serialize(),
success: function(data){alert(data)}
}); // end new Ajax.Request
});
}
function onDocumentMouseDown( event ) {
....do stuff
if (objectID < num_obj){
ajax_post()
}
if (objectID == num_obj){
$("form").submit();}
objectID ++;
$("form").clearForm();
document.forms[0].ObjectID.value = objectID;
}
</script>
<form action="" method="post" id="inputform">
<div id="ajaxwrapper">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
</div>
</form>
</body>
</html>
my view.py:
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import *
from firstapp.forms import PracForm3
num_obj=2
def index(request):
Obj.objects.all().delete()
if request.method == 'POST':
form = PracForm3(request.POST)
print "\n\n\n\n\n"
if request.is_ajax():
print "yaaaaay AJAX"
print request.POST
else:
print "noooooo"
print "\n\n\n\n\n"
if form.is_valid():
cd = form.cleaned_data
.....
if cd['ObjectID'] == num_obj:
return HttpResponseRedirect('/submit')
else:
form = PracForm3()
dic = {'form': form, 'num_obj': num_obj, ...}
return render_to_response('index.html', dic, context_instance=RequestContext(request))
my urls.py:
from django.conf.urls import patterns, include, url
from firstapp.views import index, submit
from django.conf import settings
urlpatterns = patterns('',
('^index$', index),
('^submit$', submit),
)
for some reason, my Ajax does not work anyone knows what I'm doing wrong?

This is probably related CSRF for AJAX posts, you have to remember to pass the CSRF token in as POST data with every POST request. You may need to add this code here, which are the Django docs that talk about Django+CSRF+AJAX.
I notice in your Django view that you are redirecting to "submit" Typically with ajax I would not redirect to a different url, I would use a HttpResponse and return some string value, such as JSON. I also don't know why you have print statements in your view, unless you are steeping into that view in a debugger. For testing/debugging you should have a variable that you assign some string to, and then pass that back to your ajax. Then in your ajax you can read the message from the view, something like this.
def index(request):
...
if request.is_ajax():
msg = 'it worked'
else:
msg = 'i am sad'
return HttpResponse(msg)
...
Also you javascript should have a success and error function in the ajax call, you only
have success so you are not catching errors, given the view tad bit your javascript would look like this:
...
success: function(data){alert(data)},
error: function(data){alert(data)}
...
It looks like you may have several issues going on. If you have not done the CSRF stuff that I gave you the link to that is an issue too, for testing use the csrf_exempt if you don't want to address doing it the preferred way. Try the code bits above and see if that makes progress for you.

The csrf token is included in $('#input-form') post request, so I don't think that was the issue. I think the problem is that you aren't converting the context into json in your view. This is from the docs example:
import json
from django.http import HttpResponse
class JSONResponseMixin(object):
"""
A mixin that can be used to render a JSON response.
"""
response_class = HttpResponse
def render_to_response(self, context, **response_kwargs):
"""
Returns a JSON response, transforming 'context' to make the payload.
"""
response_kwargs['content_type'] = 'application/json'
return self.response_class(
self.convert_context_to_json(context),
**response_kwargs
)
def convert_context_to_json(self, context):
"Convert the context dictionary into a JSON object"
# Note: This is *EXTREMELY* naive; in reality, you'll need
# to do much more complex handling to ensure that arbitrary
# objects -- such as Django model instances or querysets
# -- can be serialized as JSON.
return json.dumps(context)

You're requesting /index/ in your ajax call, but your pattern matches /index.

It may caused by the csrf, add #csrf_exempt before the method name will be helpful.
#csrf_exempt
def method(args):
pass

I commented out the CSRF so it wasn't that my ajax function was the problem. Here is the correct way:
function ajax_post(){
$.ajax({
type: 'POST',
url: '/index/',
data: $(this).serialize(),
success: function(data){alert(data)}
}); // end new Ajax.Request
}
thanks for all your input

Related

how do I call specific function in views.py through ajax call in javascript file?

In Django we map URLs to functions in views.py.
If we do an ajax call then we need to mention url in ajax call but that will call the function mapped to that url.
I want to call specific function from views.py via ajax call?
What should I do so that I can call any function in views.py via AJAX call without changing url?
views.py
def index(request):
codes=Code.objects.all()[:10]
context={
'name': 'KD',
'codes': codes
}
return render(request,'coding/index.html',context)
def details(request):
code=Code.objects.get(1)
context={
'code': code
}
return render(request, 'coding/details.html', context)
urls.py
from django.contrib import admin
from django.urls import path,include
from . import views
urlpatterns = [
path('',views.index, name="index" ),
path('details',views.details, name="details" ),
];
javascript
<script type="text/javascript">
$(document).ready(function() {
$("#tests").submit(function(event){
event.preventDefault();
$.ajax({
type:"POST",
url:"/details/", // what changes should be done so that I can call other function in views.py?
data: {
'video': $('#tests').val()
},
success: function(){
$('#message').html("<h2>Code Submitted!</h2>")
}
});
return false;
});
});
</script>
Just wrap function call in view and use it like usual view. For example, you have a function:
def print_hello():
print("Hello!")
You should create view:
def print_hello_view(request):
print_hello()
return HttpResponse(status=200)
And add it into urls:
path('print-hello',views.print_hello_view, name="print_hello_view"),
And in template replace ajax url by new endpoint:
{% url 'print_hello_view' %}
If you want have possibility to choose function you should add some condition.
I think what Nikitka was saying is that you can listen for the POST request in the details view and use it to run your other python function. Try something like the following:
def details(request):
# Check if there was a POST request that contains the 'video' key
if 'video' in request.POST:
# Run your python script here.
code=Code.objects.get(1)
context={
'code': code
}
return render(request, 'coding/details.html', context)
For further reading I would like to recommend Vitor Freitas's blog.
I'm not sure why you would want to do this, and from a security perspective it's a bad idea. Allowing access to arbitrary functions could be exploitable, so you should avoid it.
The ajax client will need to know in advance the names of the internal functions so that it can invoke them, and possibly also know what arguments can be passed to the function. If the client has that information, then you might as well explicitly expose the functions in urls.py and leave it at that.
Having made my objection, you could implement a dispatch view function that accepts the request and delegates to a function specified in the request using globals()['function_name'].
Assuming that the functions to be called are view functions that return an appropriate value (rendered HttpResponse, JSON, etc.) your view might look like this:
def call_named_function(request, function_name):
try:
return globals()[function_name](request)
except KeyError:
return JsonResponse({'error': True, 'message': 'Function {!r} not found'.format(function_name)}, status=404)
except Exception, exc:
return JsonResponse({'error': True, 'message': 'Exception calling function {!r}: {}'.format(function_name, exc)}, status=500)
Add a route to urls.py to capture the function name and call the view function:
path('call/<function_name>', views.call_named_function, name='call'),
Now you can add view functions to your code, or import them from another module, and call them using the function name appended to the URL:
http://127.0.0.1:8000/call/function_1
http://127.0.0.1:8000/call/some_function
etc.
I think that it's easier to use the above, but if you do not want to change the URL, then you will have to add something to the body of the POST request that specifies the name of the function to call:
def call_named_function(request):
try:
function_name = request.POST['function_name']
return globals()[function_name](request)
except KeyError:
return JsonResponse({'error': True, 'message': 'Function {!r} not found'.format(function_name)}, status=404)
except Exception as exc:
return JsonResponse({'error': True, 'message': 'Exception calling function {!r}: {}'.format(function_name, exc)}, status=500)
with route:
path('call', views.call_named_function, name='call'),
and POST the name of the function to call in the request with name 'function_name'.
You could also POST arguments as JSON encoded lists for positional arguments and JSON encoded dicts for keyword arguments.

Is there any option where we can check the source of the AJAX request

I am working on a django project where in views i have defined a viewset which returns a queryset.
I want to use if else condition in the viewset similar to:
if AJAXrequest from xyz.js
{
//do this
}
else if AJAXrequest from abc.js
{
//do that.
}
May i please get help as i am not able to get any help from other related questions and blogs.
EDIT:
AJAX request from xyz.js calls the viewset and returns a queryset object.
AJAX request from abc.js calls the viewset with a list and returns a queryset object.
EDIT2:
class UserViewSet(ReadOnlyModelViewSet):
if request.is_ajax():
if 'user.js' in request.GET.get('users', ''):
print " called from user.js"
else:
print "called from info.js"
your ajax
$.ajax({
url: '/yoururl/',
type: 'get',
data: {'js': 'abc.js'}
})
and in your view:
def yourview(request):
if request.is_ajax():
if 'abc.js' in request.GET.get('js', ''):
# do this
else:
# do that
You can post the source information on data attribute of $.ajax function.

django view + AJAX: how to retrieve data following form POST

I have a query form that can generate a large json object from data on the server. Ideally, on submit the user should be redirected to the results page (+progress bar) that gets updated from the AJAX request until the results have been generated and are ready to display.
Currently, when the user submits the form, they're left hanging on the queryForm page whilst the results are generated. What's the correct django-way to implement a callback for dataFromQuery once it's completed?
Here is a stripped down version of my class-based view:
class QueryForm(generic.View):
form_class = ReturnQuery
template_name = 'myapp/form/queryForm.html'
def get(self, request, *args, **kwargs):
#render form
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
# <process form cleaned data>
print(request.POST)
print(form.cleaned_data)
# continues to build results object (json) which can take
# any length of time
return render(request, 'myapp/form/queryResults.html', {
'dataFromQuery': dataFromQuery,
})
I tried is_ajax() within the POST method:
if request.is_ajax():
results = { "progress" : {'progress':1} }
try:
dataFromQuery
except NameError:
dataFromQuery_exists = False
else:
dataFromQuery_exists = True
results['data']=dataFromQuery
return JsonResponse(results)
but dataFromQuery isn't being passed to the results object once it's complete.
JS:
var refreshIntervalId = setInterval(function(){
$.ajax({type: "POST",url: '/website/queryForm/', data: {csrfmiddlewaretoken : csrftoken}, dataType:'json', success: function(results){ //do something with results }})
});
I've looked at template responses but I'm not sure how they can help here as it looks like the callback is executed once the page render is complete. Any help would be appreciated.
EDIT: I may have been a little unclear. The results.progress is retrieved on successful AJAX, but this line: results['data']=dataFromQuery isn't updating once the results have been generated.
Consider using some task queue/manager, like celery. Just create task that will produce your result, monitor it somehow and display to user current progress of creating response.
HTTP works with request -> response. Django can't 'push' messages (like progress) to the browser without it asking.
What you could do is use polling, which is sending an ajax request every X seconds to get the status.
This solution is not that simple to create, as you'll need a seperate function that returns the progress, and do the actualling progressing in a seperate thread (asynchronous).
depending on the situation, a much easier solution is let your user know that data is being processed, by using a spinner for example.
I am currently developing a very similar case. What you are trying to do is not so trivial. Unfortunately Django is totally synchronous by nature, therefor your AJAX calls will be blocking on the Python side.
As already suggested use some kind of task management tool. I use celery which is easy to use and fully integrated on Django.
Your code will look like this
class QueryForm(generic.View):
...
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
token = generate_request_token()
if form.is_valid():
celery_task.apply_async(<token>, <parameters>) <-- this returns immediately
return render(request, 'myapp/form/queryResults.html', {
'token': <token>,
})
Your celery_task is now calculating the results in the background and will store the results once it has finished.
Where you want to store the results is your choice.
You still need to create another Django endpoint to get the result. Map it to something like www.yoursite.com/queryForm/data'. Something like this
def get(self, request, *args, **kwargs):
#return data in JSON format
return JsonResponse(get_data_from_storage(request.<token>))
get_data_from_storage will look into the result storage and check if the results are ready or partially ready. (Another approach is to check the status of the celery_task. When it is done, the results must be ready)
Then on the JS side, as soon as the user submit the QueryForm, he is redirected to www.yoursite.com/queryResults.html/TOKEN (result page), or find a way to pass the token to the result page.
What you can do now is to use TOKEN to query the backend for data belonging to that token. Once inside the result page you do something like.
var refreshIntervalId = setInterval(function(){
$.ajax({type: "GET", url: '/website/queryForm/data', data: {token: <token>}, dataType:'json', success: function(results){ if (results) {// do something} }})
});
The JS approach above is called continuous polling and it is not optimal. (See Socket.io for alternative, but you will need an additional component like Node.js, I went this way). The rendering is finally done on the frontend side. Each time you get new results, you update the page with fresh data.
There are other approaches, but currently this seemed the most straightforward to me.
Alternatives to Celery are:
Threads (if u know what you are doing)
RabbitMQ/ZeroMQ
Tornado (but might not be easy to integrate with Django)
Gevent

Javascript to Django views.py?

This may sound simple, but how do I send the data from a Javascript array in my index.html template to my views.py?
When the user clicks a "Recommend" button, my code calls a function that accesses my database and prints a name on the template.
def index(request):
if(request.GET.get('Recommend')):
sql_handler.recFunc()
context['name'] = sql_handler.name
return render(request, 'polls/index.html', context)
I have an array of checkbox values in Javascript that are calculated after the user presses "Recommend". I want to send it to my index view and use it as the parameter for another function.
So:
def index(request):
if(request.GET.get('Recommend')):
sql_handler.recommend()
context['name'] = sql_handler.name
//something??
tags = check_array_javascript
context['tags'] = tags
return render(request, 'polls/index.html', context)
How can I do this? I've been searching similar questions, but I'm new to Django and web development in general, so I either did not understand the answers or they didn't help me.
Alright, so for sending data from the client (JavaScript) to the backend (your Django app) you need to employ something called Ajax, it stands for Asynchronous JavaScript and XML.
Basically what it does is allowing you to communicate with your backend services without the need of having to reload the page, which, you would have to do using a normal POST or PUT form submission.
The easiest implementation is using jQuery. jQuery is first and foremost a DOM manipulation library but since its inception has grown to encompass much more than that.
A jQuery ajax call looks like this.
$(document).ready(function() {
$.ajax({
method: 'POST',
url: '/path/to/your/view/',
data: {'yourJavaScriptArrayKey': yourJavaScriptArray},
success: function (data) {
//this gets called when server returns an OK response
alert("it worked!");
},
error: function (data) {
alert("it didnt work");
}
});
});
This can then be checked for in your views.py
def index(request):
if request.is_ajax():
#do something
request_data = request.POST
return HttpResponse("OK")

Redirect after AJAX GET request in Django

I am new with Django and AJAX (javascript).
Finally I can send some arguments to a django view. This view render an edit form.
I send the parameters and the view response the form with the info that I need, but I can't to redirect to the form rendered from the view.
AJAX Fucntion:
$.ajax({
type:"GET",
url: "/recepcion/",
datatype: 'json',
data: ({i: idpro[0].innerHTML, a: ano[1].innerHTML, m: mes1[2].innerHTML }),
success: function(data){
window.location.href = data.redirect;
//window.open("GET",url,true)
}
});
django view:
def RecepcionView(request):
idp = request.GET.get('i')
anio = request.GET.get('a')
mes = request.GET.get('m')
if request.method == 'POST':
r = DetalleRecepcion.objects.get(id_proveedor=idp,anio=anio,mes=mes)
form = RecepcionForm(request.POST, instance=r)
if form.is_valid():
form.save()
return HttpResponseRedirect('/monitor/')
else:
r = DetalleRecepcion.objects.get(id_proveedor=idp,anio=anio,mes=mes)
form = RecepcionForm(instance=r)
return render_to_response('recepcion.html',
{'form':form},
context_instance=RequestContext(request))
In the firebug consoles I can see the response and is the form with the info that I need.
I don't know how to redirect to the page from the response, I am not sure if I have to add some redirect function in the django view or only in the ajax success function.
I am really new with this two language and I am confused where is the error.
Thanks in advance
I guess from your code that you have a "regular" view (not a view whose purpose is to handle ajax data). Redirecting can be achieved in the view, you do not need ajax.
Just submit the form. In your recepcion.html file write something like
some html
<form method="POST" action=""> <!-- we leave the action part blank, to send the data to the same view -->
{{ form }} <!-- the form you are passing to the template -->
{% csrf_token %} <!-- do not forget the token or django will return an error -->
<input type="submit" name="submit" value="Submit"/>
</form>
some html
Btw:
you can use render (https://docs.djangoproject.com/en/dev/topics/http/shortcuts/#render) instead of render_to_response (you will not have to pass the request every time).
you can use the reverse function to redirect to a url. Ie instead of HttpResponseRedirect('/monitor/'), write HttpResponseRedirect(reverse('name_of_the_view_you_want_to_redirect_to')), https://docs.djangoproject.com/en/1.6/ref/urlresolvers/#reverse.

Categories