django ajax POST 上的 405 错误

Posted

技术标签:

【中文标题】django ajax POST 上的 405 错误【英文标题】:405 error on django ajax POST 【发布时间】:2011-12-10 18:39:18 【问题描述】:

我有一个带有整数字段的模型,该字段会在用户点击时递增,例如“投票”按钮。

该按钮仅显示在详细信息视图中。为了增加投票计数,它发送一个 ajax POST。问题是 django 甚至在执行视图之前返回 405(不允许方法)错误。这可能是什么原因造成的?

这是我的代码:

views.py(不会被执行)

@require_POST
def vote_proposal(request, space_name):

    """
    Increment support votes for the proposal in 1.
    """
    prop = get_object_or_404(Proposal, pk=request.POST['propid'])
    proposal_form = VoteProposal(request.POST or None, instance=prop)

    if request.method == "POST" and request.is_ajax:
        if proposal_form.is_valid():
            vote = proposal_form.cleaned_data['propid']
            vote.support_votes += 1
            vote.save()
            msg = "The vote has been saved."
        else:
            msg = "The vote didn't pass validation."
    else:
        msg = "An error has ocurred."

    return HttpResponse(msg)

jQuery 代码:

<script type="text/javascript">
    function upvote(proposal) 
        var request = $.ajax(
            type: "POST",
            url: "../add_support_vote/",
            data:  propid: proposal 
        );

        request.done(function(msg) 
            var cur_votes = $("#votes span").html();
            var votes = cur_votes += 1;
            $("#votes span").html().fadeOut(1000, function()
                $("#votes span").html(votes).fadeIn();
            );
        );

        request.fail(function(jqXHR, textStatus) 
            $("#jsnotify").notify("create", 
                title:"Couldn't vote the proposal",
                text:"There has been an error." + textStatus,
                icon:"alert.png"
            );
        )
     
</script>

urls.py

urlpatterns = patterns('e_cidadania.apps.proposals.views',

    url(r'^$', ListProposals.as_view(), name='list-proposals'),

    url(r'^add/$', 'add_proposal', name='add-proposal'),

    url(r'^(?P<prop_id>\w+)/edit/$', 'edit_proposal', name='edit-proposal'),

    url(r'^(?P<prop_id>\w+)/delete/$', DeleteProposal.as_view(), name='delete-proposal'),

    url(r'^(?P<prop_id>\w+)/', ViewProposal.as_view(), name='view-proposal'),

    url(r'^add_support_vote/', 'vote_proposal'),

)

模板

<div id="votes">
    <span style="font-size:30px;text-align:center;">
         proposal.support_votes 
    </span><br/>
    <button onclick="upvote( proposal.id )" class="btn small">% trans "support" %</button>
</div>

【问题讨论】:

可能你错过了这个:docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax 我已经包含了,并且 POST 事件在平台中的其他功能上运行良好,这就是为什么我有点困惑 会不会是$.ajax中的相对URLurl: "../add_support_vote/",引起的?我可以想象,可能会调用另一个不允许 POST 的视图而不是 vote_proposal(),具体取决于您触发 Ajax 调用的页面的位置。 我也猜到了相对 url ......目前这个投票只适用于 view-proposal 页面。当您单击投票按钮时,chrome/safari 控制台会显示什么?此外,您还有一个竞争条件:如果多人同时投票,一些选票可能会丢失。改为使用字段:vote.support_votes = F("support_votes") + 1。 @jkbr 和 Rob Osborne 你是对的,view-proposal URL 与 vote_proposal 混淆了。只需更改顺序就可以了(之后我使用了字段,为了竞争条件)。如果你们俩都把你的 cmets 作为答案,我会给你赏金:) 【参考方案1】:

我收到 405 错误,因为我试图 POST 到没有 POST 方法的 TemplateView,如果这对任何人都有帮助的话。

所以我把它改成了FormView(它有一个 POST 方法),它工作了。

【讨论】:

仅供参考:我遇到了本地化问题:可能对 /ajaxurl/ 的 ajax 请求重定向到 /en/ajaxurl/(并且此重定向是 GET 而不是原始 POST)【参考方案2】:

会不会是$.ajax中的相对URLurl: "../add_support_vote/"引起的?我可以想象另一个不允许 POST 的视图可能会被调用,而不是 vote_proposal(),这取决于您触发 Ajax 调用的页面的位置。

【讨论】:

【参考方案3】:

不幸的是,Oscar 的这项小型研究没有发现问题,但希望它能帮助您阐明如何修复代码以使其正常工作。

在 main urls.py 我创建了两个视图,第一个用于按钮,第二个在 testap 中用于 ajax 调用处理程序

from django.conf import settings
from django.conf.urls.defaults import patterns, include, url    
from django.views.generic.simple import direct_to_template

urlpatterns = patterns(''    
    url(r'^$', direct_to_template , 'template':'test.html'),
    url(r'^test/', include('testapp.urls')),
)    

if settings.DEBUG:
    urlpatterns += patterns(
        '',
        url(r'^media/(?P<path>.*)$', 'django.views.static.serve', 'document_root': settings.MEDIA_ROOT, 'show_indexes': True),
)

我的带有按钮test.html 的模板为了测试目的而稍微简化了一些。还添加了csrf hook 以防止403 CSRF verification 错误:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script src=" MEDIA_URL js/jquery-1.6.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
    $(document).ajaxSend(function(event, xhr, settings) 
        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;
        
        function sameOrigin(url) 
            // url could be relative or scheme relative or absolute
            var host = document.location.host; // host + port
            var protocol = document.location.protocol;
            var sr_origin = '//' + host;
            var origin = protocol + sr_origin;
            // Allow absolute or scheme relative URLs to same origin
            return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
                (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
                // or any other URL that isn't scheme relative or absolute i.e relative.
                !(/^(\/\/|http:|https:).*/.test(url));
        
        function safeMethod(method) 
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        

        if (!safeMethod(settings.type) && sameOrigin(settings.url)) 
            xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
        
    );

       function upvote(proposal) 
           var request = $.ajax(
               type: "POST",
               url: "../test/add_support_vote/",
               data:  propid: proposal 
           );

           request.done(function(msg) 
               var cur_votes = $("#votes span").html();
               var votes = cur_votes += 1;
               $("#votes span").html().fadeOut(1000, function()
                   $("#votes span").html(votes).fadeIn();
               );
           );

           request.fail(function(jqXHR, textStatus) 
               $("#jsnotify").notify("create", 
                   title:"Couldn't vote the proposal",
                   text:"There has been an error." + textStatus,
                   icon:"alert.png"
               );
           )
        
   </script>
</head>
<body>
<div id="votes">
    <button onclick="upvote(1)" class="btn small">support</button>
</div>
</body>
</html>

来自 testapp 的urls.py 看起来像

from django.conf.urls.defaults import *
from .views import vote_proposal

urlpatterns = patterns('',
    url(r'^add_support_vote/', vote_proposal),

)

views.py 最大限度地简化了问题的本地化

from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.views.decorators.http import require_POST

@require_POST
def vote_proposal(request):
    return HttpResponse('ok')

而且它的工作原理。我有 200 个 HTTP 响应。


最后一个小建议是使用resolve 来获取处理来自ajax 调用的url 的视图函数:

from django.core.urlresolvers import resolve
resolve('/test/add_support_vote/')
# returns ResolverMatch(func=<function vote_proposal at 0x2b17230>, args=(), kwargs=, url_name='testapp.views.vote_proposal', app_name='None', namespace='')

【讨论】:

+1 关于“解决”和答案。我不知道那个功能。不幸的是,问题是 URL,由于某种原因,“view-proposal”视图与 add_support_vote 混淆了。只是改变它的工作顺序:)

以上是关于django ajax POST 上的 405 错误的主要内容,如果未能解决你的问题,请参考以下文章

django:通用类视图 + POST = HTTP 405(不允许的方法)

(浏览器作为客户端)$.ajax 上的 HTTP 405 POST 到节点服务

方法不允许 (POST) Django 405 错误

带有ajax jQuery的405(不允许方法)(POST)方法

405 Ajax POST 错误

jQuery .ajax() POST 请求抛出 405(不允许的方法)但 GET 不会