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>
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
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.
Top comments (2)
Simple and straight
Thanks!