Strange behavior of the form when using ajax

Hi all,
I have an issue when using ajax response to display toast messages (all things work fine from my perspective) but there is something strange (form have to save every fields as illustrated in the next code not as (form.save()) directly ) I don’t know why ?.


My Code


def edit_employee(request, id):
    qs = Employee.objects.select_related('user').get(id=id)
    form = EmployeeForm(request.POST or None, instance=qs)

    for f in form:
        print(
            f.name,
            f.errors
        )
        if not f.errors:
            print('No errors, from is valid and : ', request.is_ajax(), request.method)
    data = {}
    data['emp_id'] = id 
    data['error'] = 'ERROR !!!'
    data['type'] = 'error'
    if request.is_ajax() and request.method == "POST":
        # form = EmployeeForm(request.POST or None, instance=qs)
        emp_name = request.POST.get("emp_name") 
        emp_gender = request.POST.get("gender") 
        emp_depart = request.POST.get("department") 
        emp_manager = request.POST.get("emp_manager") 
        emp_phone = request.POST.get("emp_phone") 
        emp_mobile1 = request.POST.get("emp_mobile1") 
        emp_mobile2 = request.POST.get("emp_mobile2") 
        emp_email = request.POST.get("emp_email") 
        emp_identity = request.POST.get("emp_identity") 
        emp_desc = request.POST.get("emp_desc") 

        img_txt = request.POST.getlist("img-txt")     # list from request.POST 
        img_input = request.FILES.getlist("img-input") # list from request.POST 

        ajax_txt = request.POST.getlist('imgTxtValues[]')     # success to get the list from ajax response
        ajax_input = request.POST.getlist('imgInputValues[]') # success to get the list from ajax response

        emp_value = Employee.objects.values('id', 'name')
        emp_filter = emp_value.filter(name=emp_name)
        emp_obj = emp_value.get(id=id)
        match = emp_filter.exists()
        # match = Employee.objects.select_related('user').filter(name=emp_name).exists()
        if match:
            print('match found', match)
            if form.is_valid() and emp_obj['name'] == emp_name and emp_obj['id'] == id:
                print(
                    'ajax_input: ', ajax_input,
                    'ajax_txt: ', ajax_txt,

                    'IMG-TXT: ', img_txt,
                    'IMG-INPUT: ', img_input,
                    'EMP-NAME: ', emp_name,
                    'EMP-GENDER: ', emp_gender,
                    'EMP-DEPART: ', emp_depart,
                    'EMP-PHONE: ', emp_phone,
                )
            # if form.is_valid():
                save_form = form.save(commit=False)
                save_form.name = emp_name
                save_form.gender = emp_gender
                save_form.department = Departments.objects.select_related('user').get(id=emp_depart)
                save_form.department_manager = emp_manager
                save_form.identity = emp_identity
                save_form.phone = emp_phone
                save_form.mobile1 = emp_mobile1
                save_form.mobile2 = emp_mobile2
                save_form.email = emp_email
                save_form.description = emp_desc
                # save_form.user = request.user
                # save_form.updated_user = request.user
                
                save_form.save()

                if ajax_input != []:
                    [
                        EmployeeImage.objects.create(
                            employee_id=save_form.id,
                            photo=obj,
                            user = request.user,
                            updated_user = request.user,
                        ) for obj in ajax_input
                    ]
                error = ('Employee with name "%s" saved successfully' % (emp_name))
                # 'Employee name already exists !!!'
                # print(cat_name , error,)
                data['emp_id'] = id 
                data['error'] = error
                data['type'] = 'success'
                return JsonResponse(data)
            else:
                # for f in form:
                #     print('error from is not valid: ', f.name, f.errors)
                error = ('Employee with name "%s" already exists !!!' % (emp_name))
                data['emp_id'] = id 
                data['error'] = error 
                data['type'] = 'error'
                return JsonResponse(data)
        else:
            if form.is_valid():
                save_form = form.save(commit=False)
                save_form.name = emp_name
                save_form.gender = emp_gender
                save_form.department = Departments.objects.select_related('user').get(id=emp_depart)
                save_form.department_manager = emp_manager
                save_form.phone = emp_identity
                save_form.phone = emp_phone
                save_form.mobile1 = emp_mobile1
                save_form.mobile2 = emp_mobile2
                save_form.email = emp_email
                save_form.phone = emp_desc
                # save_form.user = request.user
                # save_form.updated_user = request.user
                
                save_form.save()

                if ajax_input != []:
                    [
                        EmployeeImage.objects.create(
                            employee_id=save_form.id,
                            photo=obj,
                            user = request.user,
                            updated_user = request.user,
                        ) for obj in ajax_input
                    ]
                error = ('Employee with name "%s" saved successfully' % (emp_name))
                # 'Employee name already exists !!!'
                # print(cat_name , error,)
                data['emp_id'] = id 
                data['error'] = error
                data['type'] = 'success'
                return JsonResponse(data)
            # # data['emp_id'] = None
            # print('name already exists !!!')
            # error = ('Employee with name "%s" already exists !!!' % (emp_name))
            # # 'Employee name already exists !!!'
            # # print(cat_name , error,)
            # data['emp_id'] = id #save_form.id 
            # data['error'] = error
            # data['type'] = 'error'
            # return JsonResponse(data)
    else:
        # form = EmployeeForm(request.POST or None, instance=qs)
        print('Why The Form Failed ... ')
        # error = 'Something went wrong. Please ask your administrator !!!'
        # data['emp_id'] = id #save_form.id 
        # data['error'] = error
        # data['type'] = 'error'
        # return JsonResponse(data)

    context = {
        'form': form,
        'qs': qs,
        'title': 'Edit Employee'
    }
    return render(request, 'employees/edit_employee.html', context)

Updating
forms.py

from django import forms
from .models import Employee
from apps.configurations.models import Departments

class EmployeeForm(forms.ModelForm):
    class Meta:
        model = Employee
        fields = (
            # "name",
            "phone",
            "mobile1",
            "mobile2",
            "gender",
            "address",
            "email",
            "department",            
            "department_manager",
            "identity",
            "description",

        )  

    def __init__(self, *args, **kwargs):
        super(EmployeeForm, self).__init__(*args, **kwargs)
        self.fields['department'].queryset = Departments.objects.select_related('user')

ajax script
employees/includes/edit_employee/js.html


<script>
    $(document).ready(function () {
        $.ajaxSetup({
            headers: {
                "X-CSRFToken": '{{ csrf_token }}'
            }
        });

        $(document).on('submit', "#edit-employee-form", function (e) {
            
            // let empName = $('#edit-emp-name').val(); 
            let empName = $('input[name=name]').val(); 
            let gender = $('[name=gender] option:selected').val(); 
            // let gender = $('#edit-gender option:selected').val(); 
            let department = $('[name=department] option:selected').val(); 
            // let department = $('#edit-department option:selected').val(); 
            let emp_identity = $('[name=identity]').val();
  
            var imgTxtValues = $('.img-txt').map(function() {
                return $(this).find('input').val();
            }).get(); 
            
            var imgInputValues = $('.img-input').map(function() {
                return $(this).find('input').val();
            }).get();
            
            console.log(
                'EMP-NAME :', empName,
                'IMG-TXT :', imgTxtValues,
                'IMG-INPUT :', imgInputValues,
            );

            $.ajax({
                type: 'POST',
                url: '{% url "employees:edit_employee" id=0 %}'.replace('0', {{qs.id}}), 
                // contentType: 'application/x-www-form-urlencoded;charset=utf-8',
                // dataType: 'json',
                async: true,
                cache: false,
                data: {
                   // csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val(),
                    'emp_name': empName,
                    'gender': gender,
                    'department': department,
                    'emp_identity': emp_identity,
                    'emp_manager': $('#edit-department-manager').val(),
                    'emp_email': $('[name=email]').val(),
                    'emp_phone': $('[name=phone]').val(),
                    'emp_mobile1': $('[name=mobile1]').val(),
                    'emp_mobile2': $('[name=mobile2]').val(),
                    'emp_desc': $('[name=description]').val(), 
                    'imgInputValues': imgInputValues,
                    'imgTxtValues': imgTxtValues,
                },
                success: function (data) { 
                    console.log(
                        'DATA ERROR MSG --> ', data['error'],   
                        'IMG-TXT-AFTER-SUBMITTING:: ', imgTxt, 
                        'IMG-INPUT-AFTER-SUBMITTING:: ', imgInput, 
                    );
                    
                    $(function() {
                        var Toast = Swal.mixin({
                            toast: true,
                            position: 'top-end',
                            showConfirmButton: false,
                            timer: 9000
                        });
                        var empId = data['emp_id']; 

                        if (data['type'] == 'error'){
                            console.log(data['type'])
                            toastr.error(data['error']);
                        } else if (data['type'] == 'success') {
                            toastr.success(data['error']);
                            console.log('EMP-ID: ', data['emp_id'], 'IDENTITY: ', emp_identity);
                            // setInterval('location.reload()', 10000);
                            // window.location.href = ("{% url 'employees:edit_employee' id=0 %}").replace('0', empId);
                            // localStorage.setItem('emp_id', data);
                            localStorage.setItem('emp_identity', emp_identity);
                            localStorage.getItem('emp_identity');
                            localStorage.setItem('emp_phone', $('[name=phone]').val());
                            localStorage.getItem('emp_phone');
                        } else if (data['type'] == 'info') {
                            toastr.info(data['error']);
                        }
                    });
                },
                error: function (){
                    console.log('ERROR with ajax request');
                },
            });
            e.preventDefault();
        });

    });

</script>


employees/includes/edit_employee/edit_employee.html

{% load widget_tweaks %}
{% load i18n %}

<div class="container-fluid mt--6">

    <div class="row">

        <div class="col-xl-12 order-xl-1">
            <h5 class="text-muted">{% trans "Edit Employee Info" %}</h5>
            <div class="row">
                
                <div class="col-lg-3">
                    <label class="label">
                        {% trans "Department" %}
                    </label>
                    <div class="input-group">
                        <div class="input-group-prepend">
                            <span class="input-group-text">
                                <i class="fas fa-building"></i>
                            </span>
                        </div>
                        {% render_field form.department class="form-control" id="edit-department" %}
                        <!-- <input class="form-control" name="cat-id" id="client-id" disabled> -->
                    </div>
                    <div class="text-red">{{form.department.errors}}</div>
                </div>
                <div class="col-lg-3">
                    <label class="label">
                        {% trans "Department Manager" %}
                    </label>
                    <div class="input-group">
                        <div class="input-group-prepend">
                            <span class="input-group-text">
                                <i class="fas fa-user-tie"></i>
                            </span>
                        </div>
                        {% render_field form.department_manager class="form-control" id="edit-department-manager" %}
                        <!-- <input class="form-control" name="cat-id" id="client-id" disabled> -->
                    </div>
                </div>

            </div>

            <hr class="my-4">
            <div class="row">
                
                <div class="col-lg-2">
                    <label class="label">
                        {% trans "Code" %}
                    </label>
                    <div class="input-group">
                        <div class="input-group-prepend">
                            <span class="input-group-text">
                                <i class="fas fa-hashtag"></i>
                            </span>
                        </div>
                        <input class="form-control" name="edit-emp-id" id="edit-emp-id" value="{{qs.id}}" disabled>
                    </div>
                </div>

                <div class="col-lg-4">
                    <label class="label">
                        {% trans "Name" %}
                    </label>
                    <div class="input-group">
                        <div class="input-group-prepend">
                            <span class="input-group-text">
                                <i class="fas fa-user"></i>
                            </span>
                        </div>
                        <!-- {% render_field form.name class="form-control" %} -->
                        <input class="form-control" name="name" id="edit-emp-name" value="{{qs.name}}">
                    </div>
                </div>
                
                <div class="col-lg-6">
                </div>

                <div class="col-lg-2">
                    <label class="label">
                        {% trans "Gender" %}
                    </label>
                    <div class="input-group">
                        <div class="input-group-prepend">
                            <span class="input-group-text">
                                <i class="fas fa-venus-mars"></i>
                            </span>
                        </div>
                        {% render_field form.gender class="form-control" id="edit-gender" %}
                        <!-- <input class="form-control" name="cat-id" id="client-id" disabled> -->
                    </div>
                </div>

                <div class="col-lg-4">
                    <label class="label">
                        {% trans "Email" %}
                    </label>
                    <div class="input-group">
                        <div class="input-group-prepend">
                            <span class="input-group-text">
                                <i class="fas fa-envelope"></i>
                            </span>
                        </div>
                        {% render_field form.email class="form-control" id="edit-email" %}
                        <!-- <input class="form-control" name="cat-id" id="client-id" disabled> -->
                    </div>
                </div>
                <div class="col-lg-6">
                </div>

                <div class="col-lg-6">
                    <label class="label">
                        {% trans "Identity" %}
                    </label>
                    <div class="input-group">
                        <div class="input-group-prepend">
                            <span class="input-group-text">
                                <i class="fas fa-id-card"></i>
                            </span>
                        </div>
                        {% render_field form.identity class="form-control" id="edit-identity" %}
                        <!-- <input class="form-control" name="cat-id" id="client-id" disabled> -->
                    </div>
                </div>
            </div>

            <hr class="my-4"/>
            <h4 class="text-muted"> Contact</h4>
            <div class="row">

                <div class="col-lg-2">
                    <label class="label">
                        {% trans "Phone" %}
                    </label>
                    <div class="input-group">
                        <div class="input-group-prepend">
                            <span class="input-group-text">
                                <i class="fas fa-phone"></i>
                            </span>
                        </div>
                        {% render_field form.phone class="form-control" %}
                        <!-- <input class="form-control" name="cat-id" id="client-id" disabled> -->
                    </div>
                </div>

                <div class="col-lg-2">
                    <label class="label">
                        {% trans "Mobile 1" %}
                    </label>
                    <div class="input-group">
                        <div class="input-group-prepend">
                            <span class="input-group-text">
                                <i class="fas fa-mobile"></i>
                            </span>
                        </div>
                        {% render_field form.mobile1 class="form-control" %}
                        <!-- <input class="form-control" name="cat-id" id="client-id" disabled> -->
                    </div>
                </div>

                <div class="col-lg-2">
                    <label class="label">
                        {% trans "Mobile 2" %}
                    </label>
                    <div class="input-group">
                        <div class="input-group-prepend">
                            <span class="input-group-text">
                                <i class="fas fa-mobile-alt"></i>
                            </span>
                        </div>
                        {% render_field form.mobile2 class="form-control" %}
                        <!-- <input class="form-control" name="cat-id" id="client-id" disabled> -->
                    </div>
                </div>
            </div>

            <hr class="my-4"/>
            <h4 class="text-muted"> Description</h4>
            <div class="row">
                <div class="col-lg-12">
                    <!-- <label class="label">
                        {% trans "Description" %}
                    </label> -->
                    <div class="input-group">
                        <!-- <div class="input-group-prepend">
                            <span class="input-group-text">
                                <i class="fas fa-mobile-alt"></i>
                            </span>
                        </div> -->
                        {% render_field form.description class="form-control" id="edit-description" %}
                        <!-- <input class="form-control" name="cat-id" id="client-id" disabled> -->
                    </div>
                </div>
                
            </div>
        </div>

        <div class="col-xl-12 order-xl-2">
        
            <div class="row">
            </div>
        </div>

    </div>
</div>

The main edit_employee.html

{% extends 'base.html' %}

{% load static %}
{% load i18n %}
<!-- {% load widget_tweaks %} -->


{% block content %}


<form class="" method="post" enctype="multipart/form-data" id="edit-employee-form">
    {% csrf_token %}
    {% include 'employees/includes/buttons/div_buttons.html' %}
    <section class="content">
        <div class="container-fluid">
            <div class="row">

                <!-- Starting our template tab scenario -->
                <div class="col-md-12">
                    <div class="card">

                        <!-- Add Vendor Tabs Buttons -->
                        {% include 'employees/includes/buttons/tabs_buttons.html' %}

                        <!-- /.card-body -->
                        <div class="card-body">
                            <div class="tab-content">

                                <!-- Vendor Tab -->
                                <div class="active tab-pane fade show clearfix" id="employee-tab" role="tabpanel" aria-labelledby="client-tab">
                                    {% include 'employees/includes/edit_employee/edit_employee.html' %}
                                </div>
                                <!-- /.tab-pane -->

                                <!-- Vendor Contact Tab -->
                                <div class="tab-pane fade show clearfix" id="contact-tab" v-if="hideContact" role="tabpanel" aria-labelledby="contact-tab">
                                    {% include 'employees/includes/contact/contact.html' %}
                                </div>
                                <!-- /.tab-pane -->

                                <!-- Vendor Group Tab -->
                                <div class="tab-pane fade show clearfix" id="group-tab" v-if="hideGroup" role="tabpanel" aria-labelledby="group-tab">
                                    {% include 'employees/includes/group/group.html' %}
                                </div>
                                <!-- /.tab-pane -->

                                <!-- Vendor Tax Tab -->
                                <div class="tab-pane fade show clearfix" id="tax-tab" role="tabpanel" aria-labelledby="tax-tab">
                                    <!-- out the div = [[hideProduct, hideTax]] -->
                                    {% include 'employees/includes/tax/tax.html' %}
                                </div>
                                <!-- /.tab-pane -->

                                <div class="tab-pane fade show clearfix" id="product-tab" role="tabpanel" aria-labelledby="product-tab">
                                    {% include 'employees/includes/product/add_product.html' %}
                                </div>
                                <!-- /.tab-pane -->

                                <div class="tab-pane fade show clearfix" id="accounting-tab" role="tabpanel" aria-labelledby="accounting-tab" v-if="hideAccounting">
                                    {% include 'employees/includes/accounting/accounting.html' %}
                                </div>
                                <!-- /.tab-pane -->

                                <div class="tab-pane fade show clearfix" id="contract-tab" v-if="hideContract" role="tabpanel" aria-labelledby="contract-tab">
                                    {% include 'employees/includes/contract/contract.html' %}
                                </div>
                                <!-- /.tab-pane -->

                                <div class="tab-pane fade show clearfix" id="images-tab" v-if="hideImages" role="tabpanel" aria-labelledby="images-tab">
                                    {% include 'employees/includes/images/add_images.html' %}
                                </div>
                                <!-- /.tab-pane -->

                                <!-- Settings Tab -->
                                <div class="tab-pane fade show clearfix" id="settings-tab" v-if="hideSettings" role="tabpanel" aria-labelledby="settings-tab">
                                    {% include 'employees/includes/settings/add_settings.html' %}
                                </div>
                                <!-- /.tab-pane -->
                            
                            </div>
                            <!-- /.tab-content -->
                        </div>
                        <!-- /.card-body -->
                    </div>
                    <!-- /.card -->
                </div>
                <!-- /.col -->

            </div>
        </div>
    </section>


</form>
<!-- /.form -->

{% endblock content %}
    

{% block javascripts %}

{% include 'employees/includes/edit_employee/js.html' %}

{% include 'employees/includes/images/js.html' %}

{% include 'employees/includes/vueJs.html' %}


{% endblock javascripts %}

Any suggestions

We would need to see the client-side of this. Seeing only the server-side of this is only half the picture.

We’d need to look at the form being used, how it ends up rendering as HTML, and the AJAX being used to submit it.

@KenWhitesell First of all. Thanks for your fast reply,
I’ll update my question (i’ll try to collect every snippet that make the picture clear).
Really I can save the form by above code but I miss the power of django forms

Side note: The is_ajax function is deprecated and removed in Django 4. See the 5th bullet point at Django 3.1 release notes | Django documentation | Django

Really docs stated that it is deprecated since version 3.1
I’m using LTS version 3.2

HttpRequest.is_ajax()[source]¶
Deprecated since version 3.1.

Thanks your link is helpful , I’ll try and return again to inform you by new results

Deprecated means that it will be removed, not that it has already been removed. So you’re fine using it in 3.2. However, it will have gone away the next time you upgrade.

1 Like

I updated my question ,
In forms.py file I commented the “name” field because it always give me in form errors it is required even if have a value so I used it as

<input class="form-control" name="name" id="edit-emp-name" value="{{qs.name}}">

If I’m understanding what you’ve added here, the primary problem is that the name attributes of the HTML POST data elements don’t match the field names of the Django form. That’s how Django matches the POST data to form fields.

For example, in your post data, you have 'emp_name': empName, but you don’t have an emp_name field in your form.

1 Like

OK, if we are looking at the ajax request we will find the following …

So I call it in the view as
emp_name = request.POST.get('emp_name') # 'emp_name from ajax data'
Because I defined it as
let empName = $('input[name=name]').val();
NOT
emp_name = request.POST.get('name') # as the original name of django form

Also when the field name was a part of the form in forms.py it needs to be filled even if it is already filled (I don’t know why) (I used a simple method to debug the form ),

for f in form:
       print('what is hapen to fields ', f.name, f.errors)

And it always print in the console (this field is required)
so I comment this field as it clears in my forms.py

Why I have to save the form like this

In normal forms without ajax the form saved as


save_form = form.save(commit=False)    
save_form.user = request.user          
save_form.updated_user = request.user
save_form.save()

Only the (user and updated_user) fields that didn’t include in the forms.py .
My inquiry about why I should do that to save the form , why the normal behavior disappeared

You must use the same names to submit data from the browser as the names of the fields in the form if you want the form fields to be automatically filled by Django.

In other words, if you have a field in MyFormClass named my_field, then the POST data being submitted by the browser must use the name my_field for the key of the data being submitted for that field. If you do that, then my_form = MyFormClass(request.POST) will populate my_field with the data submitted, and you won’t need to do all that excess data handling.

You can get rid of all this excess field manipulation by just submitting the POST data with the names of the fields being used in the form.

1 Like

Now I am understanding you , I will try changing the names and debug

Thanks @KenWhitesell

Thanks @KenWhitesell ,
You are right. Now my (Adding and Editing Forms) save data normally but I have to pass all fields in data attribute in ajax request with the right (field_name)

let empName = $('#emp-name').val(); 
let gender = $('#gender option:selected').val(); 
let department = $('#department option:selected').val(); 
data: {
                    csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val(),
                    'name': empName,
                    'gender': gender,
                    'department': department,
                    // 'img_txt': imgTxt,
                    // 'img_input': imgInput,
                    'identity': $('#identity').val(),
                    'department_manager': $('#department-manager').val(),
                    'email': $('#email').val(),
                    'phone': $('[name=phone]').val(),
                    'mobile1': $('[name=mobile1]').val(),
                    'mobile2': $('[name=mobile2]').val(),
                    'description': $('#description').val(), 
                    'imgInputValues': imgInputValues,
                    'imgTxtValues': imgTxtValues,
                },