问题
I have a model with a integer field wich will increment on user click, like a "vote this" button.
The button only shows on the detail view. To increment the vote count it sends an ajax POST. The problem is that django returns a 405 (method not allowed) error even before executing the view. What can be causing this?
Here is my code:
views.py (doesn't get executed)
@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 code:
<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'),
)
Template
<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>
回答1:
Couldn't the problem be caused by the relative URL url: "../add_support_vote/"
in $.ajax
? I can imagine that another view that doesn't allow POST might be called instead of vote_proposal()
depending on the location of the page from which you trigger the Ajax call.
回答2:
Oscar unfortunately this small research didn't found the issue, but hope it will help you to clarify how to fix your code to get it work.
In main urls.py
I've created two views first one for the button, the second one in testap for ajax call handler
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}),
)
My template with button test.html
a bit simplified for test purposes. Also csrf hook has been added to prevent 403 CSRF verification
error:
<!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>
urls.py
from testapp looks like
from django.conf.urls.defaults import *
from .views import vote_proposal
urlpatterns = patterns('',
url(r'^add_support_vote/', vote_proposal),
)
and views.py
which maximally simplified to localize the issue
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')
And it's works. I've got 200 HTTP response.
Also final small suggestion to use resolve
to get view function which handle url from ajax call:
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='')
回答3:
I was getting a 405 error because I was trying to POST to a TemplateView
which doesn't have a POST method, if that helps anyone.
So I changed it to a FormView
instead (which has a POST method), and it worked.
来源:https://stackoverflow.com/questions/7819206/405-error-on-django-ajax-post