owendace
owendace

Reputation: 21

Django - csrf token not defined

What this code is supposed to do is let a user click they're description and be able to edit it. I have the modal popping up, but the save button will not save the data and produces the following error:

Uncaught ReferenceError: csrftoken is not defined
at HTMLButtonElement.<anonymous> (modalShortListDescription.js:6)
at HTMLButtonElement.dispatch (jquery.min.js:3)
at HTMLButtonElement.r.handle (jquery.min.js:3)

Here's where the modal is called:

<div class="tab-content col-xs-12">
{% for list in lists %}
    <input type="hidden" id="idList" id_list="{{list.id}}">
    {% if forloop.first and not createTabActive %}
    <div role="tabpanel" class="tab-pane fade active in" id="list{{list.id}}">
    {% else %}
    <div role="tabpanel" class="tab-pane fade" id="list{{list.id}}">
    {% endif %}
        <div class="content col-xs-12">
            <div class="form-horizontal sort-by col-xs-12">
                <h3>Description</h3>
                    {% if list.description %}
                        <a href="#" data-toggle="modal" data-target="#modalDescription{{list.id}}" id="editDescription">{{list.description}}</a>
                    {% else %}
                        <a href="#" data-toggle="modal" data-target="#modalDescription{{list.id}}">None</a>
                    {% endif %}
                {% include "layout/popUp/modal-short-list-description.html" %}
                </div>

Here's the modal itself:

<div class="modal fade" id="modalDescription{{list.id}}" role="dialog">
  <div class="modal-dialog">
    <form class="form-horizontal" action="{% url 'update-list-description' %}" method="post">
    {% csrf_token %}
    <!-- Modal content-->
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal">&times;</button>
        <h4 class="modal-title">Description</h4>
      </div>
      <div class="modal-body modal-body-exper modal-body-value modal-body-t">
          <div class="lineEnterValue lineTeamSize lineTitle">
              <div class="form-group {% if form.description.errors %} has-error{% elif form.is_bound %} has-success{% endif %}">
                        <div class="col-sm-10">
                            <textarea name="{{ form.description.html_name }}" class="form-control" id="{{ form.description.id_for_label }}" rows="5" style="margin: 0px; height: 90px; width: 455px;"></textarea>
                        </div>
                        {% if form.description.errors %}
                        <ul class="col-sm-10 col-sm-offset-2 error-list text-danger">
                        {% for error in form.description.errors %}
                            <li>{{ error|escape }}</li>
                        {% endfor %}
                        </ul>
                        {% endif %}
                    </div>
          </div>
      </div>
      <div class="modal-footer modal-footer-value">
        <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
        <button type="submit" class="btn btn-primary" id="description_save">Save</button>
    </div>
  </div>
</form>
</div>

Here is the .js that the save button uses:

$(document).ready(function() {
    $("#description_save").click(function() {
        var description = $("#form.description").val();
        var idList = $("#idList").attr("id_list");
        var url = "/bid/update-list-description";
        csrftoken();
        $.ajax({
            type: "POST",
            url: url,
            data: {description : description, idList: idList},
        }).done(function(response){
            $(".modalDescription").modal("hide");
            $(".editDescription").text(description);
        });
    })
})

EDIT: views.py:

@csrf_protect
def updateListDescription(request):
    checkEmployer(request)
    pageClass="my-short-lists search-for-prospect"

    #shortList = get_object_or_404(List, id = request.POST.get("idList"))
    shortList = request.user.profile.profile_employers.employer_lists.filter(pk=request.POST.get("idList"))

    if request.method =="POST":
        form = ListForm(request.POST)
        if form.is_valid():
            shortList.description = form.cleaned_data["description"]
            shortList.save()
    else:
        form = ListForm()

    return redirect('my-short-lists')

Upvotes: 2

Views: 9281

Answers (3)

WesternGun
WesternGun

Reputation: 12728

EDIT:

I think the problem lies in not only csrftoken, but also in the button: if a button calls ajax, it should not be submit. If it posts the form, it should not do ajax call. It seems that you add the token in the form, but ajax does his thing first... So the first answer seems valid.

Or,

You can instead add a header to every ajax call in $.ajaxSetup(). The DOC has this part explained:

  1. Define the getCookie(name) method;
  2. Define var csrftoken = getCookie('csrftoken');;
  3. Use these lines:

That is:

function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }

$.ajaxSetup({
   beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});
  1. Then you don't have to change any ajax call. The header is attached in every ajax call.

https://docs.djangoproject.com/en/2.0/ref/csrf/, under "Ajax" section.

I have used this approach and it worked.

Upvotes: 1

kartikmaji
kartikmaji

Reputation: 966

Edit your js to this

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 = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

$(document).ready(function() {
    $("#description_save").click(function() {
        var description = $("#form.description").val();
        var idList = $("#idList").attr("id_list");
        var url = "/bid/update-list-description";
        var csrftoken = getCookie('csrftoken');
        $.ajax({
            type: "POST",
            url: url,
            data: {description : description,
                   idList: idList,
                   csrfmiddlewaretoken: csrf_token
            },
        }).done(function(response){
            $(".modalDescription").modal("hide");
            $(".editDescription").text(description);
        });
    })
})

Upvotes: 0

The AJAX POST doesn't include the csrf_token. Add:

'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val()

into $.ajax data (along with description and idList) and remove csrftoken().

Upvotes: 0

Related Questions