#django #django-views #voting #http-status-code-405
#django #django-просмотры #голосование #http-status-code-405
Вопрос:
У меня есть модель с целочисленным полем, которое будет увеличиваться при нажатии пользователем, например, кнопка «проголосовать за это».
Кнопка отображается только в подробном представлении. Чтобы увеличить количество голосов, он отправляет сообщение ajax. Проблема в том, что 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>
Комментарии:
1. возможно, вы пропустили это: docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
2. У меня это включено, и событие POST отлично работает с другими функциями платформы, поэтому я немного смущен
3. Не может ли проблема быть вызвана относительным URL
url: "../add_support_vote/",
в$.ajax
? Я могу себе представить, что вместоvote_proposal()
того, чтобы зависеть от местоположения страницы, с которой вы запускаете вызов Ajax, может быть вызвано другое представление, которое не разрешает POST.4. Я бы также предположил относительный URL… в настоящее время это голосование будет работать только со страницы просмотра предложения. Что говорит консоль Chrome / safari, когда вы нажимаете на кнопку голосования? Кроме того, у вас есть условие гонки: если несколько человек проголосуют одновременно, некоторые голоса могут быть потеряны. Вместо этого используйте поля: vote.support_votes = F(«support_votes») 1 вместо.
5. @jkbr и Роб Осборн, вы были правы, URL-адрес для просмотра предложения путался с vote_proposal . Просто изменение порядка работало нормально (и после этого я использовал поля ради условия гонки). Если вы оба разместите свои комментарии в качестве ответов, я дам вам награду 🙂
Ответ №1:
Не может ли проблема быть вызвана относительным URL url: "../add_support_vote/"
в $.ajax
? Я могу себе представить, что вместо vote_proposal()
того, чтобы зависеть от местоположения страницы, с которой вы запускаете вызов Ajax, может быть вызвано другое представление, которое не разрешает POST.
Ответ №2:
Оскар, к сожалению, это небольшое исследование не выявило проблему, но надеюсь, что оно поможет вам уточнить, как исправить ваш код, чтобы заставить его работать.
В 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-хук для предотвращения 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 amp;amp; 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) amp;amp; 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
из testapp выглядит так
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
для получения функции просмотра, которая обрабатывает URL-адрес из вызова ajax:
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. 1 на «разрешить» и ответ. Я не знал об этой функции. К сожалению, проблема заключалась в URL-адресе, по какой-то причине представление «view-proposal» путалось с add_support_vote. Просто изменив порядок, он сработал 🙂
Ответ №3:
Я получал ошибку 405, потому что я пытался опубликовать в a TemplateView
, у которого нет метода POST, если это кому-нибудь поможет.
Поэтому я изменил его на a FormView
вместо (который имеет метод POST), и это сработало.
Комментарии:
1. Просто для информации: у меня возникла проблема с локализацией: вероятно, запрос ajax в /ajaxurl/ сделал перенаправление на /en/ajaxurl/ (и это перенаправление было ПОЛУЧЕНО вместо исходного сообщения)