This post cross-published with OnePublish
What's up DEV Network?
In this quick tutorial I am going to show you how to POST Django form without refreshing page using AJAX.
I am assuming that you already created your project. Let's start with creating our very simple Post model in models.py
from django.db import models
class Post (models.Model):
title = models.CharField(max_length=50)
description = models.TextField()
def __str__(self):
return self.title
Once you created open views.py and insert following code:
from django.shortcuts import render
from django.http import JsonResponse
from .models import Post
def create_post(request):
posts = Post.objects.all()
response_data = {}
if request.POST.get('action') == 'post':
title = request.POST.get('title')
description = request.POST.get('description')
response_data['title'] = title
response_data['description'] = description
Post.objects.create(
title = title,
description = description,
)
return JsonResponse(response_data)
return render(request, 'create_post.html', {'posts':posts})
As you see we imported JsonResponse which is an HttpResponse subclass that helps to create a JSON-encoded response. It is default Content-Type header is set to application/json. The first parameter, data, should be a dict instance. We will use JSON data to display created post right away.
I highly recommend to take look following StackOverflow question to have a better understanding difference between request.POST[] and request.POST.get()
What is the difference between
request.POST.get('sth')
and
request.POST['sth']
Did not find the similar question, both work the same for me, suppose I can use them separately but maybe I am wrong, that is why I am asking. Any ideas?
If you have more than one form in your page you can separate them by using action, so your view will not get multiple requests at the same time.
To create and save an object in a single step, we are using the create() method.
Let's take a look our html form now
<form method="POST" id="post-form">
{% csrf_token %}
<div class="form-group">
<label>Title</label>
<input type="text" class="form-control" id="title" placeholder="Title">
</div>
<div class="form-group">
<label>Description</label>
<textarea class="form-control" id="description" placeholder="Description"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
As you know we are using csrf_token to make post request and it is a just simple bootstrap form. We give id for each input to get values with AJAX by id.
$(document).on('submit', '#post-form',function(e){
$.ajax({
type:'POST',
url:'{% url "create" %}',
data:{
title:$('#title').val(),
description:$('#description').val(),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val(),
action: 'post'
},
success:function(json){
document.getElementById("post-form").reset();
$(".posts").prepend('<div class="col-md-6">'+
'<div class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">' +
'<div class="col p-4 d-flex flex-column position-static">' +
'<h3 class="mb-0">' + json.title + '</h3>' +
'<p class="mb-auto">' + json.description + '</p>' +
'</div>' +
'</div>' +
'</div>'
)
},
error : function(xhr,errmsg,err) {
console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
}
});
});
Note that you have to include jquery-2.2.4.min.js to your static files, you can take it from my git repository.
Alright! What is going on here? Initially, it is important to add id property to your form, so jQuery can detect form submission. AJAX type specifies the type of request which is post because we are sending data to database and url specifies the URL to send the request to. Then, we are using val() method to get the values of form elements by id and sending it with data parameter which specifies data to be sent to the server. It is necessary to get value of csrf_token otherwise it will cause 403 Forbidden error. As you see we specified action to let our view detect which form is submitting.
Once request successfully sent, we are cleaning form and appending our new post right away into the posts row.
The .prepend() method inserts the specified content as the first child of each element in the jQuery collection (To insert it as the last child, use .append()).
If some error occurs while sending request (I hope not😅) then the last function will add this error information to the console, so you can see what caused this nasty error. I guess you already know how to display error to user nicely.
At the end you will get following result
Mission Accomplished!
You just learned how to POST Django form using AJAX 🚀🚀
You can clone or download this project from my git repository and don't forget to follow and support me on twitter and instagram, join Reverse Astronauts community!👨🚀 See you next tutorial!
Top comments (7)
I have problems with some selects that are dependent, they have a lot of information and I have cleaned them before loading them to fill them out by ajax. but when sending them it gives an error of the values saying that they are not valid
controlc.com/24a2fecc
"If you have more than one form in your page you can separate them by using action, so your view will not get multiple requests at the same time."
Any chance we can get an example of this? Like: how does the view.py code change to accommodate this, and how does the .js also change? I've experimented a bit, but I'm kind of lost.
what about "data-name" attr? You can assigment an attribute that starts with "data-" to separate forms in ajax section.
For instance:
//html
//js
"""
data: {'content_id': $(this).attr('data-animal-type'),
'operation':'add_new_animal',
//We will got "fish" value.
"""
w3schools.com/tags/att_data-.asp
Bruh.. You forgot to write e.preventDefault() in ajax request
Thank you. I tried more 20 samples but just this work for me..
nice but where is the validation? you are not validating the inputs on the server side i think getting data from the post request and then using them directly in the model is extremely dangerous
other than post method where do you display the posts in template that you are rendering.