从 Django 中的表单发送 DELETE 请求

Posted

技术标签:

【中文标题】从 Django 中的表单发送 DELETE 请求【英文标题】:Sending a DELETE request from a form in Django 【发布时间】:2015-08-08 07:43:07 【问题描述】:

我正在尝试,获取 jQuery 的帮助,参考下面的链接;

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

我有以下脚本;

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

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

(我打算)按以下表格行事;

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

还有以下中间件;

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        

我已将它添加到我的 settings.py 中的 MIDDLEWARE_CLASSES 列表中;

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',
)

这是第 3 个条目,在 CsrfViewMiddleware 之前,考虑了 @Daniel Roseman 的话。

最后在基于类的视图中,我使用 delete 方法删除了相应的对象;

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")                      
            )
    .
    .

我希望中间件和脚本会向视图发送 DELETE 请求,但事情并没有进展。正在发送 POST 方法本身。 Django 找到了一个无效的表单并给了我回复; “表格无效!” (else: return HttpResponse("Form Invalid!")

更新

我已经更新了脚本的样子;

<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>

我将对象作为 station 传递给模板,并且我在脚本 (var station_id = station.id ) 中使用了 Django 模板语言,灵感来自 https://***.com/a/6008968/4672736

结果是一样的:/

更新

调试js发现浏览器没有找到jQuery文件。现在正确配置了静态文件,并且在 jquery.cookie.js 的帮助下设置了 csrf 保护,我的模板中有以下代码;

<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>

现在我在前端没有错误,但还没有成功。表单的提交给我一个200 响应而不是302

AJAX 请求是否会出现这种情况?

似乎该请求尚未作为删除请求传递给视图。

我尝试将success: function()alert('Object deleted!') 添加到$.ajax 并弹出警报。

更新

最后一个障碍是我必须在传递的自定义标题中使用破折号而不是下划线。我将headers: 'X_METHODOVERRIDE': 'DELETE' 更改为headers: 'X-METHODOVERRIDE': 'DELETE' 。这些字母也不必以大写字母传递。无论如何,Django 会将它们转换为大写字母。所以x-methodoverridex-MeThODoveRrIDE 也可以。我不知道为什么用破折号代替下划线。我发现当其他标题通过时带有破折号而不是下划线。

之前中间件没有找到密钥 HTTP_X_METHODOVERRIDE。现在确实如此,我的视图收到了删除请求:)

【问题讨论】:

你能把你的基于类的视图的代码也贴出来吗? @baxeico 请查看更新 你的javascript还是错了,应该是$('#delete').submit(function(e) 。我想您的 javascript 控制台中会出现错误。您是否在 Firefox 或 Chrome Devtools 上使用 Firebug 来调试您的客户端代码?你应该... ;) 【参考方案1】:

问题在于您的 javascript 代码。您将表单作为常规 POST 请求提交,根本不使用 $.ajax javascript 代码。

您应该在单独的脚本标签中包含 jquery,如下所示:

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

然后在另一个脚本标签中放置你的代码:

<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>

在这里,我假设您在这样的表单字段中拥有要删除的对象的 ID:

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

请注意您的 Ajax 调用中的 Django CSRF 保护,您应该编写一些 javascript 来在请求中设置您的 CSRF cookie 值,如 Django documentation 中所述。

同时将 HttpResponseRedirect 返回到 Ajax 调用没有多大意义,因为浏览器不会像常规 POST 请求那样重定向到该页面。

我建议先多研究一下 jquery 和 Ajax 请求的工作原理,然后再深入研究像这样的更复杂的示例。 ;)

【讨论】:

你是天使 :)。开始使用 devtools,它说找不到 jQuery 文件。我把它和模板文件放在一起。在同一目录中。我会在 w3 学校教程中阅读这样做。因此。我应该把它改到哪里? 只是给你看; ls templates/; audio_files.html home_audio_file.html jquery-1.11.3.js auth home_audio_file.html~ list_channels.html create_channel.html home_station.html stations.html deny_user.html home_station.html~ deny_user.html~ home_user.html可以在这里找到对应的模板文件“home_station.html”和jquery文件“jquery-1.11.3.js 为了简化事情,我更新了答案以从 Internet 上的 CDN 获取 jquery(假设您的客户可以访问 Internet)。但我建议你看看Django manages static files 是如何工作的。您不能只将它们放在模板目录中。 非常感谢。最终假设!现在已经读取并配置了静态文件。现在表格没有发布。它告诉我Failed to load resource: the server responded with a status of 403 (FORBIDDEN)。我认为这可能与您之前提到的 js 有关,需要在 AJAX 请求中设置 CSRF cookie 值。让我阅读并尝试弄清楚。 大功告成。现在请求得到了200 响应。它似乎没有进入视图。 200 是 AJAX 请求的预期响应吗?请查看我的更新。【参考方案2】:

这可能是您的中间件的顺序。收到请求后,中间件会按照设置中指定的顺序进行处理。在您的情况下,这意味着首先运行 CSRF 中间件,看到请求是没有 CSRF 令牌的 POST,然后引发错误。

尝试将 HttpPostTunnelingMiddleware 移到 CsrfViewMiddleware 之前。

【讨论】:

不是这样的。我得到的是if form.valid: so_and_so else: HttpResponse("Form Invalid") 的响应。我按你说的试过了,但结果是一样的。我尝试取出 csrf_token 并得到 Forbidden (403) CSRF verification failed. Request aborted. 错误。我想也许你认为这是我得到的错误。也许不吧。无论如何,中间件的顺序似乎不是问题

以上是关于从 Django 中的表单发送 DELETE 请求的主要内容,如果未能解决你的问题,请参考以下文章

Django 视图从应该发送 POST 的表单接收 GET 请求

从 Vue.js 向 Django 发送 POST/DELETE 请求时禁止(未设置 CSRF cookie。)

使用 angular 向 django-rest 发送 DELETE 请求被解释为 OPTIONS

从 django 中的发布请求中获取数据非表单数据 [关闭]

Django如何将数据从视图发送到模板

Django 设置 cookie 中的 csrftoken