1
votes

I'm currently learning in Django, and in this fetch request, I always get the same error. How could I avoid it?

topup.html


            let data = new FormData();

            data.append('amount', document.getElementById('amount').value);

            data.append('csrfmiddlewaretoken', "{{ csrf_token }}");



            var response = fetch('{% url "secret" %}', {
              method: 'POST',
              body: data,
              credentials: 'same-origin',

            })

views.py

def secret(request):
    amount = request.POST["amount"]

error:

MultiValueDictKeyError at /secret 'amount' Request Method: GET Request URL: http://127.0.0.1:8000/secret Django Version: 3.0.6 Exception Type: MultiValueDictKeyError Exception Value:
'amount' Exception Location: C:\Users\eric3\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\utils\datastructures.py in getitem, line 78 Python Executable: C:\Users\eric3\AppData\Local\Programs\Python\Python38-32\python.exe Python Version: 3.8.2

Django Error

I would really appreciate some help.

Thanks!

1

1 Answers

1
votes

You get MultiValueDictKeyError exception when opening that URL, because when request.POST["amount"] is evaluated, it founds no item within the POST dictionary with a key named 'amount'.

Apparently, you want secret view to be a view that is going to be reached only through ajax calls, not to be accessed by the user. If you don't want that, to get rid of that exception being raised when non-ajax requests reach the view, then do the following:

def secret(request):
    amount = request.POST.get("amount")
    # ... all your other code

Now it doesn't matter if your view is accessed by a user or by an ajax call, that exception is gone.

On the other hand, the documentation advises to use the canonical csrf token included in the cookies, when performing POST method requests through ajax. Make sure that you have put 'X-CSRFToken' within the HTTP request headers to do so.

The JavaScript code should look more or less as follows:

/* Create FormData instance and append the field that you want */
let data = new FormData();
data.append('amount', document.getElementById('amount').value);

/* Perform the fetch call */
fetch("{% url 'secret' %}", {
    method: 'POST',
    body: data,
    headers: {'X-CSRFToken': csrftoken}
    })
    /* Do whatever you do when a response (successful or not) has been received */
    .then(response => {
        console.log(response);
    })


/* Get csrf cookie */
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');

As said in the docs, you can simplify a lot the process of getting the cookie by using the JavaScript Cookie Library