AJAX, which stands for asynchronous JavaScript and XML, is a set of technologies used on the client-side to send and retrieve data from the server asynchronously.
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.
Contents
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:
- Create an XMLHttpRequest object.
- Use the XMLHttpRequest object to exchange data asynchronously between the client and the server.
- Use JavaScript and the DOM to process the data.
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.
The general structure of the Fetch API looks like this:
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)
});
Refer to Using Fetch and WindowOrWorkerGlobalScope.fetch() from the MDN Documentation for more examples along with the full options available for the
fetch
method.
When Should You Use AJAX?
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:
- Search auto-completion
- Form validation
- Table sorting and filtering
- Captchas
- 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:
Method | URL | Description |
---|---|---|
GET | /todos/ |
Returns all todos |
POST | /todos/ |
Adds a todo |
PUT | /todos/<todo-id>/ |
Updates a todo |
DELETE | /todos/<todo-id>/ |
Deletes a todo |
The example project can be found on GitHub:
- Fetch version: https://github.com/testdrivenio/django-ajax-xhr
- jQuery version: https://github.com/testdrivenio/django-ajax-xhr/tree/jquery
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())
.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
Equivalent jQuery code:
$.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:
from django.http import HttpResponseBadRequest, JsonResponse
from todos.models import Todo
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 == '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);
});
We need to specify how we want to send the credentials in the request.
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.
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:
Forbidden (CSRF token missing or incorrect.): /todos/
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:
$.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
from django.http import HttpResponseBadRequest, JsonResponse
from todos.models import Todo
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)
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:
- POST -
/todos/
- 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: {
"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
from django.http import HttpResponseBadRequest, JsonResponse
from django.shortcuts import get_object_or_404
from todos.models import Todo
def todo(request, todoId):
# request.is_ajax() is deprecated since django 3.1
is_ajax = request.headers.get('X-Requested-With') == 'XMLHttpRequest'
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()
return JsonResponse({'status': 'Todo updated!'})
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
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
Django View
View:
from django.http import HttpResponseBadRequest, JsonResponse
from django.shortcuts import get_object_or_404
from todos.models import Todo
def todo(request, todoId):
# request.is_ajax() is deprecated since django 3.1
is_ajax = request.headers.get('X-Requested-With') == 'XMLHttpRequest'
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.
The example project can be found on GitHub:
- Fetch version: https://github.com/testdrivenio/django-ajax-xhr
- jQuery version: https://github.com/testdrivenio/django-ajax-xhr/tree/jquery