Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sending a DELETE request from a form in Django

Tags:

jquery

django

I am trying to send a DELETE request from a form in Django taking help from jQuery referring following link below;

https://baxeico.wordpress.com/2014/06/25/put-and-delete-http-requests-with-django-and-jquery/

What I have are the following scripts;

<script src="jquery-1.11.3.js"></script>

<script>
  $(document).ready(function(){
    $.ajax({
      id : 'delete',
      headers : {'X_METHODOVERRIDE': 'DELETE'}
    });
  });
</script>

(I intend to have) acting on the following form;

<form method="post" id="delete" name="delete" action="">
  {% csrf_token %}
  <input type="submit" value="Delete" />
</form>

And the following middleware;

from django.http import QueryDict

class HttpPostTunnelingMiddleware(object):
    def process_request(self, request):
        if request.META.has_key('HTTP_X_METHODOVERRIDE'):
            http_method = request.META['HTTP_X_METHODOVERRIDE']
            if http_method.lower() == 'put':
                request.method  = 'PUT'
                request.META['REQUEST_METHOD'] = 'PUT'
                request.PUT = QueryDict(request.body)
            if http_method.lower() == 'delete':
                request.method  = 'DELETE'
                request.META['REQUEST_METHOD'] = 'DELETE'
                request.DELETE = QueryDict(request.body)
        return None        

I have it added to the MIDDLEWARE_CLASSES list in my settings.py;

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'stationrunner.middleware.HttpPostTunnelingMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

It's the 3rd entry, before the CsrfViewMiddleware taking words of @Daniel Roseman into consideration.

And finally in my class based view I have the delete method delete the corresponding object;

class StationHome(View):
    .
    .
    .
    def post(self,request, pk):
        station = Station.objects.get(pk=pk)
        form = StationForm(request.POST, instance=station)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse("home_station",
                                                kwargs={'pk':pk},
                                            )
                                    )
        else:
            return HttpResponse("Form Invalid!")

    def delete(self, request, pk):
        Station.objects.get(pk=pk).delete()
        return HttpResponseRedirect(
            reverse("list_create_station")                      
            )
    .
    .

I expect the middleware and the script make a DELETE request sent to the view but things ain't flying. A POST method itself is being sent. Django is finding an invalid form and gives me the response; "Form Invalid!" (else: return HttpResponse("Form Invalid!")

Update

I have updated the script to look like;

<script>
  $(document).ready(function(){
    $('#delete').submit.function(e){
      e.preventDefault();
      var station_id = {{ station.id }}
      $.ajax({
        type: 'POST',
        url: '/station/' + station_id,
        headers: {'X_METHODOVERRIDE': 'DELETE'}
      });
    });
  });
</script>

I have the object passed to the template as station and I have used the Django template language inside the script (var station_id = {{ station.id }}) taking inspiration from https://stackoverflow.com/a/6008968/4672736

The result is the same :/

Update

Debugging js found that the browser wasn't finding the jQuery file. Now with static files properly configured and also, the csrf protection set with help of jquery.cookie.js I have the below code in my template;

<script src="{% static "stationrunner/jquery-1.11.3.js" %}"></script>
<script src="{% static "stationrunner/jquery.cookie.js" %}"></script>

<script>
  $(document).ready(function(){
    var csrftoken = $.cookie('csrftoken');
    function csrfSafeMethod(method) {
      // these HTTP methods don't 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);
        }
      }
    });
    $('#delete').submit(function(e){
      e.preventDefault();
      $.ajax({
        type: 'POST',
        url: '{% url 'home_station' station.id %}',
        headers: {'X_METHODOVERRIDE': 'DELETE'}
      });
    });
  });
</script>

Now I have no errors on the front end but success ain't met yet. The submission of the form gets me a 200 response instead of 302.

Is this expected with an AJAX request?

It seems the request is not yet getting passed to the view as a delete request.

I tried adding success: function(){alert('Object deleted!')} to $.ajax and the alert do pop up.

Update

The final hurdle was that I had to use dashes in the custom header passed instead of underscores. I changed headers: { 'X_METHODOVERRIDE': 'DELETE' } to headers: { 'X-METHODOVERRIDE': 'DELETE' }. The letters need not be passed in capital letters too. Django will anyway convert them to capitals. So x-methodoverride or x-MeThODoveRrIDE for that matter is fine too. I don't know why dashes instead of underscores though. I figured that out as the other headers passed were carrying dashes and not underscores.

Earlier the middleware wasn't finding the key HTTP_X_METHODOVERRIDE. Now it does and my view gets a delete request :)

like image 648
Afzal S.H. Avatar asked Oct 20 '22 10:10

Afzal S.H.


1 Answers

The problem is with your javascript code. You are submitting the form as a regular POST request and not using the $.ajax javascript code at all.

You should include jquery in a separate script tag, like this:

<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>

Then in another script tag you put your code:

<script>
    $('#delete').submit(function(e) {
        // do NOT submit the form as a regular POST request
        e.preventDefault();
        var object_to_delete_id = $('#object_to_delete_id').val();
        $.ajax({
            type: 'POST',
            url: '/your-view-url/' + object_to_delete_id,
            success: function() {
                alert('Object deleted!')
            },
            headers: { 'X_METHODOVERRIDE': 'DELETE' }
        });
    });
<script>

Here I'm assuming that you have the id of the object you want to delete in a form field like this:

<form method="post" id="delete" name="delete" action="">
  {% csrf_token %}
  <input type="text" id="object_to_delete_id" />
  <input type="submit" value="Delete" />
</form>

Please pay attention to Django CSRF protection on your Ajax call, you should write some javascript to set your CSRF cookie value in the request, as explained in the Django documentation.

Also returning a HttpResponseRedirect to an Ajax call doesn't make much sense, because the browser will not redirect to that page as it would happen with a regular POST request.

I'd suggest to study a little bit more how jquery and Ajax requests work, before digging in more complex examples like this. ;)

like image 116
Augusto Destrero Avatar answered Oct 21 '22 23:10

Augusto Destrero