Django AJAX Form submission is always invalid

I have a Django Project with a view that contains two forms that I want to be submitted with two separate ajax requests. The first ajax request is actually supposed to fill in one value of the second form which this does successfully. Also, the second form has a few initial values that I later plan on making hidden so the user only sees the necessary fields to fill in after submitting the first form.

I am passing in an “ajax_type” keyword after checking for “request.is_ajax()” and "request.method == 'POST’ within my view to determine if the ajax type is the first ajax request or the second one. My second ajax request makes use of a model form that is supposed to create a new instance of my model upon validation.

My View:

def canadaTaxEntries(request):

    newest_record = LAWCHG.objects.aggregate(Max('control_number'))
    newest_control_number = newest_record["control_number__max"]
    print(newest_control_number)

    today = date.today().strftime("1%y%m%d")
    current_time = time.strftime("%H%M%S")
    #print(today)
    #print(current_time)

    initial_data = {
        'control_number': (newest_control_number + 1),
        'item_number': None,
        'vendor_tax_code': None,
        'expiration_flag': 1,
        'charge_category': None,
        'charge_desc_code': None,
        'country': 'US',
        'state': None,
        'city': None,
        'county': None,
        'charge_amount': None,
        'charge_uom': 'CA',
        'charge_percentage': 0.0,
        'apply_flag': 1,
        'beg_del_date': None,
        'end_del_date': None,
        'rev_date': today,
        'rev_time': current_time,
        'est_date': today,
        'est_time': current_time,
        'program_id': 'BI008',
        'operator_id': 'FKRUGER',
    }

    canada_tax_form = CanadaTaxForm(request.POST or None, initial=initial_data)

    if request.is_ajax():
        if request.method == 'POST':
            canada_tax_form = CanadaTaxForm(request.POST or None, initial=initial_data)

            data = json.load(request)
            #item_number = request.POST.get('itm', False)
            ajax_type = data.get("ajax_type")

            if ajax_type == "screen_item":
                print('1st request')
                num = data.get("item")
                itmnum = int(num)
                return screen_item_ajax(itmnum)
            
            elif ajax_type == "tax_entry":
                print("2nd request")

                if canada_tax_form.is_valid():
                    print("VALID")
                    canada_tax_form.save()
                else:
                    print("NOT VALID")
                    print(canada_tax_form.errors)

                controlnum = data.get("control_num")
                itemnum = data.get("item")
                ventaxnum = data.get("vend_tax")
                return canada_tax_entries_ajax(request, controlnum, itemnum, ventaxnum)


    context = {
        "canada_tax_form":canada_tax_form,
    }

    return render(request, "html/canadaTaxEntries.html", context)

My ajax requests:

$(document).on('submit', '#item_num_form', function (e) {
    let item_number = document.getElementById("item_number").value

    console.log(item_number)
    e.preventDefault()
    $.ajax({
        url: "http://127.0.0.1:8000/canada/",
        type: 'POST',
        dataType: "json",
        data: JSON.stringify({
            ajax_type: "screen_item",
            item: item_number,
        }),
        headers: {
            "X-Requested-With": "XMLHttpRequest",
            "X-CSRFToken": getCookie("csrftoken"),
        },
        success: function (data) {
            console.log(data)
            console.log("AJAX Worked!")

            let description = data.item_info[0].item_description;
            document.getElementById("description").innerHTML = "Item Description: " + description;

            let upc_num = data.item_info[0].upc_14_long;
            document.getElementById("upc_num").innerHTML = " UPC Number: " + upc_num;

            document.getElementById("id_item_number").value = item_number;

            function getTH() {
                const column = table_headers;
                const head = document.querySelector('thead');
                let tags = "<tr>";
                for (i = 0; i < column.length; i++){
                    tags += `<th>${column[i]}</th>`;
                }
                tags += "</tr>"
                head.innerHTML = tags;

                getTD();
            }

            function getTD() {
                const body = document.querySelector('tbody');
                let tags = "";

                data.existing_tax_records.map(d => {
                    tags += `<tr>
                    <td>${d.beg_del_date}</td>
                    <td>${d.end_del_date}</td>
                    <td>${d.charge_amount}</td>
                    <td>${d.charge_uom}</td>
                    <td>${d.apply_flag}</td>
                    <td>${d.charge_category_id}</td>
                    <td>${d.charge_desc_code_id}</td>
                    <td>${d.vendor_tax_code}</td>
                    <td>${d.control_number}</td>
                    </tr>`
                })
                body.innerHTML = tags;
            }
            getTH()
        }
    })
})


$(document).on('submit', '#tax_form', function (e) {
    let control_number = document.getElementById("id_control_number").value
    let item_number = document.getElementById("id_item_number").value
    let vendor_tax_code = document.getElementById("id_vendor_tax_code").value

    console.log(control_number)
    e.preventDefault()
    $.ajax({
        url: "http://127.0.0.1:8000/canada/",
        type: 'POST',
        dataType: "json",
        data: JSON.stringify({
            ajax_type: "tax_entry",
            control_num: control_number,
            item: item_number,
            vend_tax: vendor_tax_code,
        }),
        headers: {
            "X-Requested-With": "XMLHttpRequest",
            "X-CSRFToken": getCookie("csrftoken"),
        },
        success: function (data) {
            console.log(data)
            console.log("2nd AJAX Worked!")
            console.log("Is form valid???")
        },
        error: function (error) {
            console.log(error)
            console.log("2nd AJAX Failed")
        }
    })
})

Both my ajax requests are returning a success message so the trigger to submit my second form is being hit. I think the problem has to do with my form being wiped before I submit it even though I have all the input fields filled when hitting the submit button for the second form. I am not sure if I am initializing my form incorrectly but I am stumped as to why my form never passes the validation check.

Finally printing out to the console with “print(canada_tax_form.errors)” after the validation check prints out the following which leads me to believe my form is being wiped. How could I avoid this?

NOT VALID
<ul class="errorlist"><li>control_number<ul class="errorlist"><li>This field is required.</li></ul></li><li>item_number<ul class="errorlist"><li>This field is required.</li></ul></li><li>vendor_tax_code<ul class="errorlist"><li>This field is required.</li></ul></li><li>expiration_flag<ul class="errorlist"><li>This field is required.</li></ul></li><li>charge_category<ul class="errorlist"><li>This field is required.</li></ul></li><li>charge_desc_code<ul class="errorlist"><li>This field is required.</li></ul></li><li>country<ul class="errorlist"><li>This field is required.</li></ul></li><li>state<ul class="errorlist"><li>This field is required.</li></ul></li><li>charge_amount<ul class="errorlist"><li>This field is required.</li></ul></li><li>charge_uom<ul class="errorlist"><li>This field is required.</li></ul></li><li>charge_percentage<ul class="errorlist"><li>This field is required.</li></ul></li><li>apply_flag<ul class="errorlist"><li>This field is required.</li></ul></li><li>beg_del_date<ul class="errorlist"><li>This field is required.</li></ul></li><li>end_del_date<ul class="errorlist"><li>This field is required.</li></ul></li><li>rev_date<ul class="errorlist"><li>This field is required.</li></ul></li><li>rev_time<ul class="errorlist"><li>This field is required.</li></ul></li><li>est_date<ul class="errorlist"><li>This field is required.</li></ul></li><li>est_time<ul class="errorlist"><li>This field is required.</li></ul></li><li>program_id<ul class="errorlist"><li>This field is required.</li></ul></li><li>operator_id<ul class="errorlist"><li>This field is required.</li></ul></li></ul>

HttpRequest.is_ajax was deprecated in Django 3.1 and removed in Django 4. You should not be trying to use it in your view.

There is nothing you can examine that will guarantee that a request is coming via AJAX. From the perspective of the server, a GET is a GET. You should be including some type of data yourself to identify the nature of the call.

That’s not true, a GET is not a GET if Headers are involved.

request.is_ajax() is no longer used, but you can check the headers.

Replace

if request.is_ajax():

by

if request.headers.get('X-Requested-With') == 'XMLHttpRequest':

But make sure you have it in your Javascript:

headers: {
                    'X-Requested-With': 'XMLHttpRequest',  // Important for Django to recognize the AJAX request
                },

Nope, sorry.

I can use any number of browser-based tools to alter headers being sent. I can also use tools like curl, wget, or requests to send requests with custom headers.

Adding a header to your AJAX request is not a guarantee that the request comes from an AJAX call. On the server side, you have no way to know that any given request comes from AJAX.

A GET really is just a GET.

We just want our view to detect our AJAX call. I’m not talking about altering requests, if the user isn’t simply on the browser clicking a button, then that’s a whole different story :slight_smile:

Or a better idea, let’s return a serialized object in all cases, and handle it ourselves in the front end whether it’s an AJAX call or not, that in my opinion is the best way.