Working With AJAX in Django
Working With AJAX in Django
io /blog/django-ajax-xhr/
AJAX allow us to carry out changes to the content of a web page, without requiring a reload to the entire
page by the user. This can be useful, for example, for auto-completion in a search bar or form validation.
Used correctly, you can improve your site's performance, decrease server load, and improve the overall
user experience.
In this article, we'll look at examples of how to perform GET, POST, PUT, and DELETE AJAX requests in
Django. While the focus will be on the Fetch API, we'll also show jQuery examples.
What is AJAX?
AJAX is a programming practice that uses the XMLHttpRequest (XHR) object to communicate with a
server asynchronously and build dynamic webpages. Although AJAX and XMLHttpRequest are often
used interchangeably, they are different things.
To send and receive data to and from a web server, AJAX uses the following steps:
AJAX can be used with jQuery by using the ajax method, but the native Fetch API is much better since it
has a clean interface and doesn't require a third-party library.
fetch('http://some_url.com')
.then(response => response.json()) // converts the response to JSON
.then(data => {
console.log(data);
// do something (like update the DOM with the data)
});
1/10
Again, AJAX can help improve your site's performance while decreasing server load and improving the
overall user experience. That said, it adds a lot of complexity to your application. Because of this, unless
you're using a Single-page Application (SPA) -- like React, Angular, or Vue -- you should really only use
AJAX when it's absolutely necessary.
Some examples of when you may want to think about using AJAX:
1. Search auto-completion
2. Form validation
3. Table sorting and filtering
4. Captchas
5. Surveys and polls
In general, if the content needs to be updated a lot based on user interactions, you may want to look at
using AJAX to manage updating parts of the web page rather than the entire page with a page refresh.
CRUD Resource
The examples in this article can be applied to any CRUD resource. The example Django project uses
todos as it's resource:
GET Request
Let's start with a simple GET request for fetching data.
Fetch API
Example:
fetch(url, {
method: "GET",
headers: {
"X-Requested-With": "XMLHttpRequest",
}
})
.then(response => response.json())
2/10
.then(data => {
console.log(data);
});
The only required argument is the URL for the resource you wish to fetch the data from. If the URL
requires keyword arguments or query strings, you can use Django's {% url %} tag.
Did you notice the X-Requested-With header? This is necessary to inform the server that you are
sending an AJAX request.
fetch returns a promise containing the HTTP response. We used the .then method to first extract the
data in JSON format from the response (via response.json()) and then to access the data returned.
In the example above, we just outputted the data in the console.
https://github.com/testdrivenio/django-ajax-xhr/blob/main/static/main.js#L19-L39
jQuery AJAX
$.ajax({
url: url,
type: "GET",
dataType: "json",
success: (data) => {
console.log(data);
},
error: (error) => {
console.log(error);
}
});
https://github.com/testdrivenio/django-ajax-xhr/blob/jquery/static/main.js#L19-L41
Django View
On the Django side of things, while there are several ways to handle AJAX requests in the views, the
simplest is with a function-based view:
def todos(request):
# request.is_ajax() is deprecated since django 3.1
3/10
is_ajax = request.headers.get('X-Requested-With') == 'XMLHttpRequest'
if is_ajax:
if request.method == 'GET':
todos = list(Todo.objects.all().values())
return JsonResponse({'context': todos})
return JsonResponse({'status': 'Invalid request'}, status=400)
else:
return HttpResponseBadRequest('Invalid request')
In this example, our resource is todos. So, before getting the todos from the database, we verified that
we're dealing with an AJAX request and that the request method is GET. If both are true, we serialized
the data and sent a response using the JsonResponse class. Since a QuerySet object is not JSON
serializable (the todos, in this case), we used the values method to return our QuerySet as a dictionary
and then wrapped it in a list. The final result is a list of dicts.
https://github.com/testdrivenio/django-ajax-xhr/blob/main/todos/views.py#L13-L28
POST Request
Next, let's see how to handle POST requests.
Fetch API
Example:
fetch(url, {
method: "POST",
credentials: "same-origin",
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": getCookie("csrftoken"),
},
body: JSON.stringify({payload: "data to send"})
})
.then(response => response.json())
.then(data => {
console.log(data);
});
In the above code, we used the value of "same-origin" (the default) to indicate to the browser to send
the credentials if the requested URL is on the same origin as the fetch call.
4/10
In the case where the frontend and the backend are hosted on different servers, you'd have to set
credentials to "include" (which always sends the credentials with each request) and enable Cross-
Origin Resource Sharing in the backend. You can use the django-cors-headers package to add CORS
headers to responses in a Django app.
Want to learn more about how to handle AJAX requests on the same domain and cross
domain? Review the Django Session-based Auth for Single Page Apps article.
This time we sent the data to the server in the body of the request.
Take note of the X-CSRFToken header. Without it, you'd get a 403 forbidden response from the server in
the terminal:
That's because it's mandatory to include the CSRF token when making a POST request to prevent Cross
Site Request Forgery attacks.
We can include the CSRF token by setting the X-CSRFToken header on each XMLHttpRequest to the
value of the CSRF token.
The Django documentation simplifies our lives by providing us a nice function that allow us to acquire the
token:
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== "") {
const cookies = document.cookie.split(";");
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// 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;
}
https://github.com/testdrivenio/django-ajax-xhr/blob/main/static/main.js#L42-L56
jQuery AJAX
The AJAX POST request with jQuery is quite similar to the GET request:
5/10
$.ajax({
url: url,
type: "POST",
dataType: "json",
data: JSON.stringify({payload: payload,}),
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": getCookie("csrftoken"), // don't forget to include the
'getCookie' function
},
success: (data) => {
console.log(data);
},
error: (error) => {
console.log(error);
}
});
https://github.com/testdrivenio/django-ajax-xhr/blob/jquery/static/main.js#L44-L61
Django View
On the server side, the view needs to get the data from the request in JSON format, so you'll need to use
the json module in order to load it.
import json
def todos(request):
# request.is_ajax() is deprecated since django 3.1
is_ajax = request.headers.get('X-Requested-With') == 'XMLHttpRequest'
if is_ajax:
if request.method == 'POST':
data = json.load(request)
todo = data.get('payload')
Todo.objects.create(task=todo['task'],
completed=todo['completed'])
return JsonResponse({'status': 'Todo added!'})
return JsonResponse({'status': 'Invalid request'}, status=400)
6/10
else:
return HttpResponseBadRequest('Invalid request')
After verifying that we're dealing with an AJAX request and that the request method is POST, we
deserialized the request object and extracted the payload object. We then created a new todo and sent
back the appropriate response.
https://github.com/testdrivenio/django-ajax-xhr/blob/main/todos/views.py#L13-L28
PUT Request
Fetch API
Example:
fetch(url, {
method: "PUT",
credentials: "same-origin",
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": getCookie("csrftoken"), // don't forget to include the
'getCookie' function
},
body: JSON.stringify({payload: "data to send"})
})
.then(response => response.json())
.then(data => {
console.log(data);
});
This should look similar to a POST request. The only difference is the shape of the URL:
1. POST - /todos/
2. PUT - /todos/<todo-id>/
https://github.com/testdrivenio/django-ajax-xhr/blob/main/static/main.js#L59-L73
jQuery AJAX
Equivalent jQuery:
$.ajax({
url: url,
type: "PUT",
dataType: "json",
data: JSON.stringify({payload: payload,}),
headers: {
7/10
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": getCookie("csrftoken"), // don't forget to include the
'getCookie' function
},
success: (data) => {
console.log(data);
},
error: (error) => {
console.log(error);
}
});
https://github.com/testdrivenio/django-ajax-xhr/blob/jquery/static/main.js#L64-L81
Django View
Example:
import json
if is_ajax:
todo = get_object_or_404(Todo, id=todoId)
if request.method == 'PUT':
data = json.load(request)
updated_values = data.get('payload')
todo.task = updated_values['task']
todo.completed = updated_values['completed']
todo.save()
8/10
https://github.com/testdrivenio/django-ajax-xhr/blob/main/todos/views.py#L31-L53
DELETE Request
Fetch API
Example:
fetch(url, {
method: "DELETE",
credentials: "same-origin",
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": getCookie("csrftoken"), // don't forget to include the
'getCookie' function
}
})
.then(response => response.json())
.then(data => {
console.log(data);
});
https://github.com/testdrivenio/django-ajax-xhr/blob/main/static/main.js#L76-L89
jQuery AJAX
jQuery code:
$.ajax({
url: url,
type: "DELETE",
dataType: "json",
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRFToken": getCookie("csrftoken"),
},
success: (data) => {
console.log(data);
},
error: (error) => {
console.log(error);
}
});
https://github.com/testdrivenio/django-ajax-xhr/blob/jquery/static/main.js#L84-L100
9/10
Django View
View:
if is_ajax:
todo = get_object_or_404(Todo, id=todoId)
if request.method == 'DELETE':
todo.delete()
return JsonResponse({'status': 'Todo deleted!'})
return JsonResponse({'status': 'Invalid request'}, status=400)
else:
return HttpResponseBadRequest('Invalid request')
https://github.com/testdrivenio/django-ajax-xhr/blob/main/todos/views.py#L31-L53
Summary
AJAX allows us to perform asynchronous requests to change parts of a page without having to reload the
entire page.
In this article, you saw, in detail, examples of how to perform GET, POST, PUT, and DELETE AJAX
requests in Django with the Fetch API and jQuery.
10/10