Django And Fetch API Form Submissions –Without Page Reloading

Ridwan Yusuf
6 min readMay 25, 2021

--

A guide for submitting form using Fetch API to a Django-Backend without page refresh.

Submitting form data to the database/server without refreshing the page is an important technology in web application since it proves user experience. In such case, users don’t necessarily need to wait for a page reload to receive a response from the server.

Just before we get our hands dirty with codes, it is important that we know there a couple of ways form data can be sent to the database/server without refreshing the page. The original and working standard to achieve this is using built-in XMLHttpRequest object when using AJAX(Asynchronous JavaScript And XML.) to avoid page refresh.

AJAX is cool but it’s syntaxes can be confusing as the code base becomes larger. Jquery is another good choice to POST form to the database. However, using Fetch API or Axios Library to post form data to the backend without refreshing the page makes life easier for developers.

For the purpose of this tutorial, we will be using fetch API to submit form to a Django website.

Quick Note: This guide assumes you have fundamental knowledge with JavaScript and Django. Check our beginners’ guide to building a Django website here.

A complete source code of this tutorial can be found on GitHub here

Overview: This tutorial would be like a contact application. A user visits the website and fill out a form. The data submitted will be sent to the server without page reloading. We get response in the background and update the website visitor whether the form data is successfully submitted or not. All this happens without reloading the page.

Start a Django Project named fetch_api and start up the django development server

$ django-admin startproject fetch_api
$ python manage.py runserver

Next is to create an app named contact.

$ python manage.py startapp contact

We need to let Django be aware of this contact app we just created. To do this, go to fetch_api/settings.py and add this line.

fetch_api/settings.py

INSTALLED_APPS = [
...
'contact',
...
]

Now, let’s start configuring our application. Preferrably, We would start with the database(where the structure of the contact form is defined), then we handle the Url of the website. Finally, we handle what the user sees on the webpage.

Starting with the schema of the contact application, we head over over to the contact/models.py file to make update to it.

from django.db import models
#from django.db import models
class Contact(models.Model):
name=models.CharField(max_length=200, blank=True, null=True)
email=models.CharField(max_length=20, blank=True, null=True)
subject=models.CharField(max_length=20, blank=True, null=True)
def __str__(self):
return self.name

Let’s run command below to make relevant changes in the database.

$ python manage.py makemigrations
$ python manage.py migrate

Make change to the contact/admin.py to register the models defined in models.py to the Django Administration.

from django.contrib import admin
# Register your models here.
from .models import Contact
admin.site.register(Contact)

For now, we have set up the database stuffs, let’s look for a way to handle URL in the whole project.

Update fetch_api/urls.py which handles the whole urls in the fetch_api projec. we imported include in the 2nd line. This is used to include the urls.py file that we are going to create in the contact directory.

from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('contact.urls')),
]

Time to handle the app-level urls, to do this we need to create a contact.urls.py and mahe the changes below

contact/urls.py

from django.conf.urls import url
from . import views
namespace='contact'
urlpatterns =[
url(r'^$', views.contact, name='contact'),
]

For simplicity, we are using function-based view here. See this guide to compare Class-Based View with a Function-Based View.

Let’s define the views contact/views.py

contact/views.py

from django.shortcuts import render, HttpResponse
from . models import Contact
from django.http import JsonResponse
# Create your views here.
import json
def contact(request):
if request.method == 'POST':
name=request.POST.get('name')
email=request.POST.get('email')
subject=request.POST.get('subject')
newcontact = Contact(name=name,email=email,subject=subject)
newcontact.save()
data = {'name':name, 'email':email, 'subject':subject }
print(data)
return JsonResponse(data, safe=False)


else:
return render(request, 'contact/contactform.html', {})

In the contact/views.py, we tell Django what template to return visit the base url(http://127.0.0.1:8000/, in this case), but we have not created any. A TemplateDoesNotExist at / error would be fired up if we run the server.

Let’s fix this by creating contact.html.

Note:We structure this template by creating a folder named templates inside contact app Here is the structure: contact/templates/contact/contactform.html

contactform.html

<!doctype html>
<html lang="en">
<head>
<title> Django and Fetch API Form Submissions- No Page Refresh</title>
</head>
<body style="text-align: center; display: block">
<p id="output-message"></p>
<form action="{% url 'contact' %}" id="formdata" method="POST" role="form" class="contactForm">
{% csrf_token %}
<input type="text" name="name" id="name" class="form-control" placeholder="Your Name" required> <br><br><br>

<input type="email" class="form-control" name="email" id="email" placeholder="Your Email" required>
<br><br><br>

<input type="text" class="form-control" name="subject" id="subject" placeholder="Subject" required> <br><br>

<input type="submit" id="conctactsub" value="contact us">

</form>

Now, it is time to test our codes.

As at this point, if we visit the http://127.0.0.1:8000/, we'll see a contact form where user can input their information. When the form is submitted, the page will reload and the data get submitted to the database.

For us to prevent this default page refresh when the form is submitted, we need to use Javascript. Fetch API will be used to submit the form in the background and receive a response from the server. For simplicity, we will write our JavaScript code inside the Html file.

Below here is a basic fetch API syntax to send form data to the server. preventDefault() is used prevent the default behaviour of a submitted form. With this set up, we prevent the browser from reloading when form is submitted. We'll need to add few lines of code later so that we can use DOM manipulation to can alert the user on the status of the form data submitted.

With Fetch API, we return Promises. We catch an error with .catch()

<script type="text/javascript">
document.getElementById("formdata").addEventListener("submit", function(e){
e.preventDefault();

name = document.getElementById("name").value;
email = document.getElementById("email").value
subject = document.getElementById("subject").value

const formData = new FormData();
//console.log(name);
formData.append('name', name);
formData.append('email', email);
formData.append('subject', subject);
formData.append('csrfmiddlewaretoken', '{{ csrf_token }}');
console.log(formData);
fetch('{% url "contact" %}', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch(error => {
console.error('Error:', error);
});
});
</script>

So far so good, the form is successfully submitted to the databased without reloading. We are simply outputing the response from the server to the console. We need a way to let the users know the status of the form submitted by showing them an alert to them.

To show an alert message after the form is submited without refreshing the page, we need to add few JavaScript code to manipulate the DOM We update the as below:

<script type="text/javascript">
document.getElementById("formdata").addEventListener("submit", function(e){
e.preventDefault();
//var url = '/'
name = document.getElementById("name").value;
email = document.getElementById("email").value;
subject = document.getElementById("subject").value
const formData = new FormData();

//console.log(name);
formData.append('name', name);
formData.append('email', email);
formData.append('subject', subject);
formData.append('csrfmiddlewaretoken', '{{ csrf_token }}');
console.log(formData);
fetch('{% url "contact" %}', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
document.getElementById("name").value = "";
document.getElementById("email").value = "";
document.getElementById("subject").value = "";
document.getElementById("output-message").innerText = "Your enquiry has been sent!"
setTimeout(function(){
document.getElementById("output-message").style.display = "none";
}, 3000);
setTimeout(function(){
document.getElementById("output-message").style.display = "";
}, 1000);
})
.catch(error => {
console.error('Error:', error);
document.getElementById("output-message").innerText = "Sorry! There was an error submitting your enquiry. "
setTimeout(function(){
document.getElementById("output-message").style.display = "none";
}, 3000);
setTimeout(function(){
document.getElementById("output-message").style.display = "";
}, 1000);

});
});
</script>

An alert message that says Your enquiry has been sent! is displayed to the user if the form submission was successful. If not, an error message will be dispalyed instead(Stop the development server to mimick a network failure).

A complete source code of this tutorial is available on this github repo.

Final Thought

So far so good, we are able to submit form data to the Django server without refreshing the page. Further improvements can be added to the code. Like handling network or internet connection failure before making a request, etc.

Enjoyed this article?

Feel free to explore my video collection on YouTube for more educational content. Don’t forget to subscribe to the channel to stay updated with future releases.

--

--

Ridwan Yusuf
Ridwan Yusuf

Written by Ridwan Yusuf

RidwanRay.com -I provide engineers with actionable tips and guides for immediate use

Responses (2)