DEV Community

Cover image for Django ajax form submission with fetch api
tochimclaren
tochimclaren

Posted on

Django ajax form submission with fetch api

Following my previous post Django infinite scrolling with javascript fetch api and function based view

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

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>
Enter fullscreen mode Exit fullscreen mode

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>

Enter fullscreen mode Exit fullscreen mode

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']

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
]

Enter fullscreen mode Exit fullscreen mode

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

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 %}

Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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

Discussion (2)

Collapse
officialksolomon profile image
officialksolomon

Simple and straight

Collapse
tochimclaren profile image
tochimclaren Author

Thanks!