I am writing a django app where the user wants to click a button and have a partial page change. Data needs passed from the server to the web page without a needing a complete page refresh. That task sounded like a job for ajax. However, I can't make Ajax work in my app.
I cannot get the call into my server-side function. Below is the code the subject matter is regarding missed calls. My intent is to get the server side to return a list of missed calls and display it to the user without having to refresh the page.
When I click the button, I get a popup that says "Something goes wrong" using firebug, I traced this to a DAJAXICE_EXCEPTION but I don't know anything else about it.
What's going on here? How do I make this work? Also if there's an easier way to do this that doesn't require the Dajax library please advise. And any step-by-step examples would be very helpful.
Server side function
-------- /jim/ajax.py---------
#dajaxice_register
def missedCalls(request, user):
print "Ajax:missedCalls" #never prints...
missedCalls = ScheduledCall.objects.filter(status__exact='Missed')
render = render_to_string('examples/pagination_page.html', { 'missedCalls': missedCalls })
dajax = Dajax()
dajax.assign('#calls','innerHTML', render)
return dajax.json()
-------page.html---------
<script type='text/javascript'>
function missed_calls_callback(data){
# The dajax library wants a function as a return call.
# Have no idea what I'm supposed to do with this part of the function.
# what is supposed to go here?
alert(data.message);
}
</script>
<!-- Button -->
<input type="button" name="calltest" value="JQuery Test"
id="calltest" onclick="Dajaxice.jim.missedCalls(missed_calls_callback, {'user':{{ user }}})">
<div id="calls">
{% include "calls.html" %}
</div>
--------calls.html--------
<h2> Missed Calls</h2>
<ul>
{% for i in missedCalls.object_list %}
<li>{{ i }}</li>
{% endfor %}
</ul>
Before you start using a library, if might be helpful to do manually (to see what's going on).
An ajax request is a HTTP request like any other except that it happens asynchronously (i.e. outside the normal request/response cycle) and it usually returns json or xml (although you can return html if you like).
This means that to accept an AJAX request you just create an url and view as you would normally.
urls.py
...
url(r"^/my/ajax/path/$", myapp.views.ajax_view, name="do-something-ajaxy"),
...
views.py
def ajax_view(self, request):
# Django's Request objects have a method is_ajax()*
# which checks the header to see if it's an 'ajax' request
if request.is_ajax():
raise Http404
missedCalls = ScheduledCall.objects.filter(status__exact='Missed')
# You can return a dictionary-like json object which can be manipulated by the javascript when it receives it
return HttpResponse(simplejson.dumps(missedCalls), mimetype='application/javascript')
https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.is_ajax
And using jquery to carry out the ajax request:
(function($){
$.ajax({
type: 'GET',
url: '/my/ajax/path/',
success: function(data){
for call in data:
/* Do something with each missed call */
},
});
});
Related
I am building simple django app where I want to do some parsing when user click button on the frontend.
I have template variable {{ parsing }} which I am using inside index.html to disable button for parsing when user click on it
<div class="btn-group mr-2" role="group" aria-label="Parsing group">
<button class="btn btn-dark btn-lg" id="parseButton" {% if parsing %} disabled {% endif %}>
<i class="fa fa-terminal gradient-text"></i>
<span class="gradient-text">| Parse</span>
</button>
</div>
Next what I do is JQuery method which sends ajax request to my backend to initialize variables for parsing and method from views.py returns redirect to the same page (index.html).
$('#parseButton').click(function () {
$.ajax({
type: 'POST',
url: 'initialize_parsing/',
headers: {"X-CSRFToken": $.cookie("csrftoken")},
data: {}
});
Then my views.py:
def initialize_before_parsing(request):
if request.method == 'POST':
frontendTracker = FrontendTracker()
frontendTracker.progress = 0
frontendTracker.parsing = True
return redirect("index")
class IndexView(TemplateView):
template_name = 'index.html'
def get_context_data(self, **kwargs):
frontendTracker = FrontendTracker()
context = super(IndexView, self).get_context_data(**kwargs)
context["showAnnotation"] = frontendTracker.showAnnotationButton
context["parsing"] = frontendTracker.parsing
context["progress"] = frontendTracker.progress
return context
and urls.py
urlpatterns = [
path('', IndexView.as_view(), name="index"),
path("initialize_parsing/", initialize_before_parsing, name="initialize_before_parsing"),
]
Finally what is bother me is that when I send that ajax request and everything works fine when my page being redirected {{progres}} template variable isn't changed or any other until I do refresh.
Doing refresh with js when ajax is success isn't something which I want to do because I have some other methods inside js which I want to execute after that ajax request and that will reset my js code.
How can achieve to change value of parsing variable without refresh?
You have state in 2 places: in the browser and on the server.
When you load a page from the server then it parses the state on the server and fills in the template accordingly. When you make the AJAX call you pass state to the server, but don't do anything to the state of the page in the browser. On a subsequent reload you see the reflected change in the browser because you passed that state through the template again.
I don't know too much about FrontendTracker or your specific problem, so I don't know what the best solution is. One solution is add a .done(function( data ) { /* your code */ }) handler to the $.ajax call and then manipulate the content of the page to reflect how you want the page to change. However, it seems like you have some sort of progress value, so you likely need to make subsequent AJAX calls to get updates from the server for updated values of that.
I'm trying to do page with simple calculator. I made calculator in js, and now, when I click button I want to pass arguments from js to django. Count them there and print on redirect page.
I have problem with redirect page. When I dont put button in form, js script call calc view, but dont redirect page. I want redirect page and print a result there.
html code:
<form action="calc/" method="post">
{% csrf_token %}
<input id='btn' type="submit" value="CALC" onclick="change()">
</form>
javascript code:
function change(){
var foo = 1;
var foo1 = [1, "tralal", true, ''];
$.ajax({
url: 'calc/',
data : {
'foo': foo,
'foo1': foo1,
},
success: function (data) {
alert("it worked!");
}
}
)};
urls in django
path('calc/', views.calc, name='calc')
view in django
def calc(request):
foo = request.GET.get('foo')
print(foo)
foo1 = request.GET.getlist('foo1[]')
print(foo1)
context = {'data': foo1}
return render(request, 'calc.html', context)
If you want to redirect to another page, you have to do it explicitly from the javascript when the success callback is called. You can see here some answers on how to redirect the page from javascript. To do so, you will have to have another view for serving the result page.
The results of the calculation can be kept in the server (cached), or communicated from the client.
But, I wonder why you are using an Ajax request for that. Ajax requests are useful if you want to do some async http requests to the server without refreshing (or redirecting) the page. So I would say, don't use an Ajax request here, just simply submit the form. You can have a look for example here to see how forms work in javascript. You can also check this Django tutorial about using forms. Django has some tools for handling forms that can be quite convenient when for instance you want to update or create new instances in your database model. It can be a bit confusing at the beginning, but I think it is worth spending some time understanding Django forms.
I do something like this, it work, but I dont know is it correct.
views.py
def link(request):
global matrix
matrix = request.GET.getlist('foo1[]')
return HttpResponseRedirect('/calc/')
def calc(request):
if request.is_ajax:
global matrix
context = {'data': matrix}
return render(request, 'calc.html', context)
else:
return HttpResponseRedirect('/')
urls.py
path('link/', views.link, name='link'),
path('calc/', views.calc, name='calc'),
js function
$(document).ready(function() {
$("#btn").click(function(e) {
var foo = 1;
var foo1 = [1, "tralal", true, ''];
$.ajax({
url: 'link/',
data : {
'foo': foo,
'foo1': foo1,
},
success: function (data) {
window.location.href = "calc/"
}
});
});
});
html code
<input id='btn' type="submit" value="COUNT">
calc.html
{% for d in data %}
<p>"{{ d }}"</p>
{% endfor %}
Page redirect to calc/ and show correct not none matrix.
Result
What you get when using window.location.href = "127.0.0.1:8000/calc" is normal. You are just redirecting to the same page without any url query parameters.
So, you basically want to redirect to the same page?
Why then do you want to redirect? This does not make much sense to me. And again, if you wanna redirect, don't use AJAX, and follow the "normal" approach for submitting forms.
So what I think makes more sense, is to not redirect, but use the data you receive from the the AJAX request and show the information. You can still use some Django tempting en return some rendered HTML that you will then add dynamically with some Javascript code.
It could be something like that:
Javascript
$.ajax({
url: 'link/',
data : {
'foo': foo,
'foo1': foo1,
},
success: function (data) {
$('#result-container').html(data) // add the data in the div container
$("#your-form").trigger('reset'); //you should reset your form
}
});
Main HTML at "/" should contain
<div id="result-container"></div>
And you should just get rid of your link view and have the other somehow like that
def calc(request):
if request.is_ajax:
matrix = request.GET.getlist('foo1[]')
context = {'data': matrix}
return render(request, 'calc.html', context)
But I tell you again, you should use Django forms for what you are doing. See this tutorial for example.
I hope it helps.
I found one question similar to this, but not sure the solution is the best (it doesn't seem very DRY) and it is quite old so I hope its ok that I ask another one.
I have a django project with various pages that require you to be logged in to view them. It is quite simple to get django to redirect to a login page if you are not logged in but what I would like to do is to bring up a modal login with javascript. Obviously I could go through each link and determine manually whether it required login and assign a class to those links or something but I think there must be a better way.
I was thinking possibly along the lines of replacing the built in url template tag with my own that checks if the view is login required (not sure if this is possible) and then if it is, replaces the url with something that can be recognised by javascript to assign a click handler to bring up the modal. Ideal solutions would somehow allow for people without javascript to still be redirected to the login page.
Am I asking too much?
UPDATE
Ok, have come up with some basic code so I'm hoping that might encourage someone to point out where I am going wrong/right. I think that you probably can't check whether a view is login_required with a template tag and so perhaps a decorator for the views is the best option.
#decorators.py
def AJAX_login_required(view_func):
#wraps(view_func)
def wrapper(request, *args, **kwargs):
if request.is_ajax():
if not request.user.is_authenticated():
return HttpResponse(json.dumps({'login_required': True}), mimetype='application/json')
else:
if request.user.is_authenticated():
return view_func(request, *args, **kwargs)
else:
path = request.get_full_path()
login_url = reverse('login')
return redirect_to_login(path, login_url, 'next')
return wrapper
#views.py
#AJAX_login_required
def view_which_requires_login(request):
//etc.
def view_that_does_not(request):
//etc.
#javascript
$('a').click(function(e) {
var link = $(this).attr('href');
$.ajax({
type:"POST",
url:link,
data:'',
success: function(response){
if (response.login_required) {
//open modal login
return;
}
else {
document.location.href = link;
}
},
error: function(){
document.location.href = link;
}
});
});
I think this should result in what I want but it does require that every page that doesn't require login be effectively sent twice. Once to the ajax query and then again when the page is redirected. Is there a better way?
In terms of a better way... you could create a view that keeps a list of all your login required (or non-login-required) views. Issue an ajax call to that view, which would send a lightweight json message back. Saves your user the bandwidth, if not your server overhead.
Question is, how do you do get and keep the information? I'd try using decorators that register the view functions with a library. (This pattern is common in Django itself, see the admin and template tag system.) Make sure your library object always has a count and stashes it in the database or the cache. Don't compile it on the fly-- sounds like a good manage function.
I have looked at a number of answers and other websites, but none answer my specific question. I have a webpage with "+" and "-" buttons, which should increment a variable called "pieFact". This variable must be updated dynamically without having to refresh the page. It should then be passed to my Django view each time the value is changed. This will be used to update the size of pie charts in a web map. I have the following:
<button type="button" id=bttnMinus onclick="pieFact=pieFact*0.9">-</button>
<button type="button" id=bttnPlus onclick="pieFact=pieFact*1.1">+</button></td>
<script type="text.javascript">
var pieFact=0;
</script>
How can I pass the value of "pieFact" to Django? Based on my limited knowledge, I think I may have to use AJAX post/get.
In order to keep from refreshing the page, yes, you will need AJAX. I usually don't like to suggest libraries too much in answers, however, in the interest of being easily cross-browser compatible, I would suggest the use of jQuery.
With jQuery it would be as simple as
Inside of your django template
<html>
...
<head>
<script>
var URL = "{% url 'my_view_that_updates_pieFact' %}";
</script>
</head>
...
Later on...
You'll need to either POST or GET the data to the server via AJAX. To be more RESTful, whenever I need to send data to the server I use POST. jQuery provides the $.post() convenience function to AJAX data to a url via POST. The three parameters are the URL, the data to send (as a JavaScript object; think python dictionaries if you're not too familiar with JavaScript), and a callback function once the server sends back a response.
<script>
function updatePieFact(){
var data = {'pieFact': pieFact};
$.post(URL, data, function(response){
if(response === 'success'){ alert('Yay!'); }
else{ alert('Error! :('); }
});
}
The .click() functions are basically the same thing as specifying onlick in the html attribute. Both click events update pieFact as you would expect and then call updatePieFact() to send the value of pieFact to the server.
$(document).ready(function(){
$('#bttnMinus').click(function(){
pieFact *= 0.9;
updatePieFact();
});
$('#bttnPlus').click(function(){
pieFact *= 1.1;
updatePieFact();
});
});
</script>
In views.py
Since I've used the $.post() function in the JavaScript, the request that Django is going to receive is going to have a method of "POST", so I check to make sure that the method is indeed POST (this means that if someone visits the URL for this view with something like a GET request, they won't update anything). Once I see that the request is, in fact, a POST, I check to see if the key 'pieFact' is in the dict request.POST.
Remember when I set the variable data in the javascript to {'pieFact': pieFact}? That javascript just becomes the request.POST python dictionary. So, if in the javascript I had instead used var data = {'hello': pieFact};, then I would be checking if 'hello' in request.POST instead. Once I see that pieFact is in the request.POST dictionary, I can get its value and then do something with it. If everything is successful, I return an HttpResponse with the string 'success'. This correlates with the check in javascript: if(response === 'success').
def my_view_that_updates_pieFact(request):
if request.method == 'POST':
if 'pieFact' in request.POST:
pieFact = request.POST['pieFact']
# doSomething with pieFact here...
return HttpResponse('success') # if everything is OK
# nothing went well
return HttpRepsonse('FAIL!!!!!')
Hopefully that will get you pointed in the right direction.
I want to add some Ajax-niceness to my Django-coded website.
In my Django code, I use the #login_required decorator from django.contrib.auth.decorators to mark which view requires authentication. The default behavior when a not authenticated user clicks it is to redirect him/her to login page, and then pass the target page.
What I saw on some sites, and really liked, is that when user clicks a link leading to a place restricted to logged-only users, instead of getting redirected to a login page, he/she gets a popup window (via JavaScript) asking him/her to log in or register. There's no redirection part, so no need for a user to use the "back" key if he/she decides he/she really doesn't like the website enough to waste the time registering.
So, the qestion is: how would you manage the task of automatically marking some links as "restricted" so JavaScript can handle their onclick event and display a "please log in" popup?
I am facing the same issue, and, like you, I would like a simple decorator to wrap around a Django ajax view in order to handle authentication in the same way that I have other views. One approach that seems promising to me is to use such a decorator in conjunction with JavaScript that looks for a certain value in the response.
Here is first revised draft of the decorator:
from functools import wraps
def ajax_login_required(view_func):
#wraps(view_func)
def wrapper(request, *args, **kwargs):
if request.user.is_authenticated():
return view_func(request, *args, **kwargs)
json = simplejson.dumps({ 'not_authenticated': True })
return HttpResponse(json, mimetype='application/json')
return wrapper
Here is the view:
#ajax_login_required
def ajax_update_module(request, module_slug, action):
# Etc ...
return HttpResponse(json, mimetype='application/json')
And here is the JavaScript (jQuery):
$.post('/restricted-url/', data, function(json) {
if (json.not_authenticated) {
alert('Not authorized.'); // Or something in a message DIV
return;
}
// Etc ...
});
EDIT: I've attempted to use functools.wraps, as suggested. I have not actually used this decorator in working code, so beware of possible bugs.
Sounds like a page template possibility.
You could pass a LINK_VIA (or something) that you provide as onClick="return popup(this, 'arg')" or None. Each link would be <A HREF="link" {{LINK_VIA}}>some text</a>.
For anonymous sessions, LINK_VIA has a value.
For logged in sessions, LINK_VIA is None
You could use an {% if %} statement around your <A HREF=...> tags. This seems wordy.
You could write your own custom tag with for {% link_via %}. I'm not familiar enough with this, but you can provide the link and text as strings and your tag can generate one of two kinds of links.
I would agree with S.Lott
Make a check in the template, if the user is logged in, just put the link as usual, if not, put something like
<a href="{{link}}" onclick="return login_popup()">
where login_popup would return false if the user says cancel.
This could be probably be done much easier in Jinja2 through its macros.
If the template doesn't know which urls require the user to login, you probably need to re-consider your design.
If you must, I guess you can do the same thing that the django url dispatcher does to discover the view function.
see: django.core.urlresolvers
once you've grabbed the view function you can check if it's decorated with #login_required.
This would be done in a custom tag probably.
If you use Jinja2, you won't need the tag, just implement the function and expose it to the Environment, it's simple but you'll have to do a bit of reading on the API of Jinja2)
Built off of Eric Walker's solution, but for Django 2.0
# Standard Imports
import functools
import django.http
def ajax_login_required(view_func):
#functools.wraps(view_func)
def wrapper(request, *args, **kwargs):
if request.user.is_authenticated:
return view_func(request, *args, **kwargs)
return django.http.JsonResponse('Unauthorized', status=401, safe=False)
return wrapper
Here is proposed version of the decorator with wrap.__doc__ , wrap.__name__
from functools import wraps
def ajax_login_required(function):
def wrap(request, *args, **kwargs):
if request.user.is_authenticated():
return function(request, *args, **kwargs)
json = simplejson.dumps({ 'not_authenticated': True })
return HttpResponse(json, mimetype='application/json')
wrap.__doc__ = function.__doc__
wrap.__name__ = function.__name__
return wrap