Django ajax form submission with fetch api
Tags: django, fetch, ajax, widgettweaks
Published: 2021-08-14 00:14:31

Following my previous post [Django infinite scrolling with javascript fetch api and function based view](https://dev.to/tochimclaren/django-infinite-scrolling-with-javascript-fetch-api-and-function-based-view-47fo)

I will be covering how to do hassle free django ajax form submission with fetch api and django form.
the code is available over here. [link](https://github.com/tochimclaren/django-infinite-scroll/tree/ajax-form-submission)

**Setting templates.**

Moving on from the previous tutorial, add the following code in template directory of the app;


`blog/templates/post/partials/navigation.html`

Create a directory named partials; inside it, paste this bootstrap navbar html component, we going to include this in the base html template which we will create in a minute. We don't want to clutter the base template.
```
<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <div class="container-fluid">
    <a class="navbar-brand" href="/">Posts</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
      <ul class="navbar-nav">
        <li class="nav-item">
          <a class="nav-link" href="{% url 'create' %}">Create</a>
        </li>
      </ul>
    </div>
  </div>
</nav>
```
after that create a base.html inside the blog template directory

`blog/templates/post/base.html`

Add the following html template
```
{% load static %}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="{% static 'blog/css/bootstrap.min.css' %}">
    <title>My Blog</title>
    <style>
    #content {
        margin: 0 auto;
        width: 40vw;
    }
    </style>
</head>

<body>
	{% block navigation %}
        {# include the navigatio #}
	{% include 'post/partials/navigation.html' %}
	{% endblock %}
    <div id="notify">
           
    </div>
    {% block content %}
    {% endblock %}

    <script src="{% static 'blog/js/bootstrap.bundle.min.js' %}"></script>
    {% block script %}
    {% endblock %}
</body>

</html>

```
open `blog` and create a new python file `forms.py`
`blog/forms.py`
inside `blog/forms.py` add the below code

```
from django import forms
from .models import Post


class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content']

```

in `views.py` import the PostForm

```
from .forms import PostForm
from django.forms import model_to_dict
from django.http import JsonResponse
from django.shortcuts import render
```

add the following code to `views.py`

```
def post_create(request):
    form = PostForm()
    if request.method == "POST":
        form = PostForm(request.POST or None)
        if form.is_valid():
            instance = form.save(commit=False)
            instance.save()
            # converts Post instance to dictionary so JsonResponse can serialize it to Json
            return JsonResponse(
                model_to_dict(instance, fields=['title']), status=201)
        else:
            return JsonResponse(form.errors, safe=False, status=200)

    ctx = {
        'form': form
    }
    return render(request, 'post/post_create.html', ctx)
```

in `blog/urls.py` add the post_create view function

```
from django.urls import path
from . import views

urlpatterns = [
    path('', views.posts, name="posts"),
    path('create/', views.post_create, name="create") # new
]

```
before we create a new template install `django-widget-tweaks`
`pipenv install django-widget-tweaks`

widget-tweaks allows you to add html attributes in the templates using it's render template tags [django-widget-tweaks](https://pypi.org/project/django-widget-tweaks/)

add it to your `INSTALLED_APPS` like this `widget_tweaks`

in `blog/templates/post` create new html template `post_create.html`
`blog/templates/post/post_create.html`

```
{% extends "post/base.html" %}
{% load static %}
{% load widget_tweaks %}
{% block content %}
	<form action="" method='post' class="mx-auto w-75" id="PostCreateForm">
	    <h1 class="h1 ms-auto">Create Post</h1>
	    {% csrf_token %}
		    {% for field in form %}
			    <label for="{{field.id_for_label}}" class="my-2">{{field.label_tag}}</label>
			    {{ field.errors }}
			    {% render_field field class="form-control" %}
		    {% endfor %}
	    <input type="submit" value="submit" class="btn btn-outline-dark mt-2">
	</form>
{% endblock %}

{% block script %}
    <script type="text/javascript" src="{% static 'blog/js/create.js' %}"></script>
{% endblock %}

```
in your `blog/static/blog/js` create a new js file with name `create.js`

```
blog/static/blog/js/create.js

const postForm = document.querySelector("#PostCreateForm");


function handleSubmit(postForm) {
    postForm.addEventListener("submit", e => {
        e.preventDefault();
        formData = new FormData(postForm);
        fetch('/create/', {
                method: 'POST',
                body: formData,
            })
            .then(response => response.json())
            .then(data => {
                postForm.reset();
                document.querySelector("#notify").innerHTML = `<div class="alert alert-info alert-dismissible fade show" role="alert">
                                                                  <strong>Success!</strong> ${data.title} <strong>saved</strong>.
                                                                  <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
                                                                </div> `
            })
            .catch((error) => {
                console.error('Error:', error);
            });
    })
}

handleSubmit(postForm)
```
And we are done! upcoming tutorial we will add image field in Post model and do some validation with ajax... stay tuned.