Django AJAX post not working with CSRF - ajax

I have this ajax like vote on my article model:
#csrf_exempt
#login_required
def like(request):
args = {}
if request.method == 'POST':
user = request.POST.get('user')
lu= request.user
post_id = int(request.POST.get('post_id'))
try:
liked_article = Article.objects.get(id = post_id)
except:
liked_article = None
if ArticleLike.objects.filter(liker=lu.id, article=post_id).exists():
liked_article.likes -=1
liked_article.save()
ArticleLike.objects.filter(article=post_id, liker=lu.id).delete()
else:
liked_article.likes +=1
liked_article.save()
newliker = ArticleLike(article=post_id, liker=lu.id)
newliker.save()
args.update(csrf(request))
args['likes'] = str(liked_article.likes)
return render(request, 'ajax_like.html', args)
Here is the ajax snippet:
$(function(){
$('#like').click(function(){
$.ajax({
type: "POST",
url: "/article/like/",
data: {
'post_id': $(this).attr('name'),
'csrfmiddlewaretoken': '{{csrf_token}}'
},
success: likeSuccess,
dataType: 'html'
});
});
});
function likeSuccess(data, textStatus, jqXHR)
{
$('#like_count').html(data);
}
and ajax_lik.html
<p id="like_count">
{% if likes > 0 %}
{{likes}}
</p>
{% else %}
wow
</i>
{% endif %}
The view works perfectly well with #csrf_exempt decorator active, but with csrf, I get 403 Forbidden error.
I tried different tweaks but could not figure out how solve this, so I appreciate your help.

If you are using a #csrfexempt decorator then you don't need to send a csrf_token. BTW, csrf_token shouldn't be part of the POST data,
data: {
'post_id': $(this).attr('name'),
'csrfmiddlewaretoken': '{{csrf_token}}'
}
it should be in the request's header "X-CSRFToken".
Here a little example to set custom headers to an ajax request using jquery (docs here):
$.ajax({
url: "/article/like/",
data: { post_id: $(this).attr('name') },
type: "POST",
beforeSend: function(xhr){xhr.setRequestHeader('X-CSRFToken', "{{csrf_token}}");},
success: likeSuccess,
dataType: 'html'
});
You can see Django's docs for this here

Related

Ajax in Django : url not found

I am coding a small Django project where an user can select an object and save it in a database. I am trying to implement an Ajax call on a button to delete this object if necessary.
I am doing it step by step, debugging with the console.
my urls:
app_name = 'register'
urlpatterns = [
path('<int:user_id>/', views.account, name='account'),
path('/delete/', views.delete, name='delete'),
]
my view.py:
def delete(request):
data = {'success': False}
if request.method=='POST':
product = request.POST.get('product')
print(product)
data['success'] = True
return JsonResponse(data)
my ajax.js:
$("#form_id").on('submit', function(event) {
event.preventDefault();
var product = 'coca-cola'
console.log('ok till this point')
$.ajax({
url: '{% url "register/delete" %}',
type: "POST",
data:{
'product':product,
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val()
},
datatype:'json',
success: function(data) {
if (data['success'])
console.log('working fine')
}
});
});
My view isn't doing much for now but I haven't any knowledge about Ajax and I am doing it one step at a time.
This is the error I get in the console:
jquery.min.js:2 POST http://127.0.0.1:8000/register/6/%7B%%20url%20%22register/delete%22%20%%7D 404 (Not Found)
As far as I understand, Ajax can't find my url: '{% url "register/delete" %}'.
I have tried '{% url "register:delete" %}' with no luck either.
I found an answer after some tweaking, I defined my url before the Ajax call and then pass it in it:
$("#form_id").on('submit', function(event) {
event.preventDefault();
var product = 'coca-cola'
var url = '/register/delete/'
console.log( url)
$.ajax({
url: url,
type: "POST",
data:{
'product':product,
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val()
},
datatype:'json',
success: function(data) {
if (data['success'])
console.log('working fine')
}
});
});
Also you can add just the string of url to "url" parameter without characters {% url %}. Maybe you copied the code from pattern Django and added it to JS-file. So it does not work.

Django Ajax Form submit wrongly redirect to another page

When I use ajax to submit a comment form in Django,the page will redirect to a blank page shows me the success data:
{"status":"success", "msg":"添加成功"}
,but not stay in current page.I want the page stay in current page and show me the new comment.
Here is my update_comment view:
def update_comment(request, news_pk):
news = get_object_or_404(News, id=news_pk)
comment_form = CommentForm(request.POST or None)
if request.method == 'POST' and comment_form.is_valid():
if not request.user.is_authenticated:
return render(request, 'login.html', {})
comments = comment_form.cleaned_data.get("comment")
news_comment = NewsComments(user=request.user, comments=comments, news=news)
news_comment.save()
# return redirect(reverse('news:news_detail', kwargs={'news_pk': news.id}))
return HttpResponse('{"status":"success", "msg":"添加成功"}', content_type='application/json')
else:
return HttpResponse('{"status":"fail", "msg":"添加失败"}', content_type='application/json')
Here is my ajax:
$(document).on('submit', 'comment_form', function(e){
e.preventDefault();
$.ajax({
cache: false,
type: "POST",
url:"{% url 'operation:update_comment' news.id %}",
data:{'news_pk':{{ news.id }}, 'comments':comments},
async: true,
beforeSend:function(xhr, settings){
xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
},
success: function(data) {
if(data.status == 'fail'){
if(data.msg == '用户未登录'){
window.location.href="login";
}else{
alert(data.msg)
}
}else if(data.status == 'success'){
window.location.reload();//refresh current page.
}
},
});
});
Here is my form:
<form id="comment_form" action="{% url 'operation:update_comment' news.id %}" method="POST" >
{% csrf_token %}
<textarea id="comment_textarea"name="comment"></textarea>
<input type="submit" value="Submit"> </input>
</form>
Finally I made it!Thanks Lord!Very excited!
I have Three major issues in my previous code.
First:Since the ajax will post the news_pk to the view update_comment,so I don't need add news_pk in this view's url and template(in the url of <form> tag and the url in the ajax),so I removed them,or the data will still pass through Form but not ajax.
Second:My binding is incorrect,I have the click handler on the form it should be a submit handler. If I was binding it to a button then I'd use click a handler.Ajax not work in Django post
But for this part I'm still a some confused,between the button summit way and form submit way.
The third issue is I mistaked 'comments' and 'comment'.'comment' is the name attribute of <textarea> ,through which forms.py gets the data.
comments is defined by ajax through var comments = $("#js-pl-textarea").val(), so in the view I need use comments = request.POST.get("comments", "") but not comment,that's the reason why 'post failed'.
Following is my code.
Here is the ajax:
$("#comment_form").submit(function(){
var comments = $("#js-pl-textarea").val()
$.ajax({
cache: false,
type: "POST",
url:"{% url 'operation:update_comment' %}",
data:{'news_pk':{{ news.pk }}, 'comments':comments},
async: true,
beforeSend:function(xhr, settings){
xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
},
success: function(data) {
if(data.status == 'fail'){
if(data.msg == '用户未登录'){
window.location.href="login";
}else{
alert(data.msg)
}
}else if(data.status == 'success'){
window.location.reload();//refresh current page.
}
},
});
return false;
});
Here is my udate_comment view:
#login_required
def update_comment(request):
news_pk = request.POST.get("news_pk", 0)
comments = request.POST.get("comments", "")
if int(news_pk) > 0 and comments:
news_comments = NewsComments()
news = News.objects.get(id=int(news_pk))
news_comments.news = news
news_comments.comments = comments
news_comments.user = request.user
news_comments.save()
return HttpResponse('{"status":"success", "msg":"添加成功"}', content_type='application/json')
else:
return HttpResponse('{"status":"fail", "msg":"添加失败"}', content_type='application/json')
Here is my form in template:
<form id="comment_form" action="{% url 'operation:update_comment'%}" method="POST" >
{% csrf_token %}
<textarea id="js-pl-textarea"name="comment"></textarea>
<input type="submit" value="Submit"> </input>
</form>
I really appreciate everyone's reply!With your reply I figured out these issue step by step!
I have something similar in my project. Its a script to like a song. I'm just gonna put the relevant codes here.
The ajax script. I put this script in a separate file named like_script.html. I call it in a template using django template include
<script>
$('#like').click(function(){
$.ajax({
type: "POST",
url: "{% url 'song:like_song' %}",
data: {'pk': $(this).attr('pk'), 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: "json",
success: function(response) {
alert(response.message);
},
error: function(rs, e) {
alert(rs.responseText);
}
});
})
</script>
The django view
import json
from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST
#login_required
#require_POST
def song_like_view(request):
if request.method == 'POST':
user = SiteUser.objects.get(user=request.user)
pk = request.POST.get('pk', None)
song = get_object_or_404(Song, pk=pk)
if song.likes.filter(pk=user.pk).exists():
song.likes.remove(user)
song.like_count = song.likes.count()
song.save(update_fields=['like_count'])
message = "You unstarred this song.\n {} now has {} stars".format(song.title, song.like_count)
else:
song.likes.add(user)
song.like_count = song.likes.count()
song.save(update_fields=['like_count'])
message = "You starred this song.\n {} now has {} stars".format(song.title, song.like_count)
context = {'message' : message}
return HttpResponse(json.dumps(context), content_type='application/json')
The url
urlpatterns = path("like/", views.song_like_view, name='like_song'),
The template where the script is called
<a class="btn btn-sm btn-primary" href="" id="like" name="{{ song.pk }}" value="Like"></i> Unstar</a>
{% include 'like_script.html' %}
Same button for like and unlike. I hope you can follow the logic to make yours right. Notice that in your view you don't need to include the pk. Just get it from the POST data pk = request.POST.get('pk', None)

ajax post request to view

I am trying to make a simple ajax request to a view but keep having a not Found error for the given url:
Not Found: /myapp/start_session/1/scan_ports/
"POST /myapp/start_session/1/scan_ports/ HTTP/1.1" 404 5711
js
$(document).ready(function() {
$.ajax({
method: 'POST',
url: 'scan_ports/',
data: {'csrfmiddlewaretoken': '{{csrf_token}}'},
success: function (data) {
alert("OK!");
},
error: function (data) {
alert("NOT OK!");
}
});
});
url
url(r'^scan_ports/$',views.ScanPorts,name='scan_ports'),
view
#login_required
def ScanPorts(request):
user = request.user
if request.method == 'POST':
currentSetting = models.UserSetting.objects.filter(isCurrent=True)
if currentSetting.serialPort:
print("GOT IT!")
return HttpResponse('')
Is the ajax request not set properly?
Assuming you are in the "myapp" app, replace:
method: 'POST',
url: 'scan_ports/',
for this:
method: 'POST',
url: '/myapp/scan_ports/',
First check your urls, the url on which you posted is incorrect hence 404 not found error.
Try to define your url in your JS as: {% url 'scan_ports' %} which will search your urls with the name your provided in urls.py
In addition, this may not be a good approach to submit a form via ajax.
Your JS should be something like this:
$('.form-class-name').on('submit', function (e) {
e.preventDefault();
console.log("ajax is called");
$.ajax({
type: $(this).attr('method'),
url: "/url-name/",
data: $(this).serialize(),
dataType: 'json',
success: function(data) {
alert("success");
},
error: function(data) {
alert("Failure");
}
})
}
e.preventDefault() prevents the natural/default action, and prevents from submitting the form twice.
.serialize(), serializes the form data in a json format.
append a "/" before and after your action url.
Your view must return a dictionary as ajax deals with JSON format.
Edit your view like this:
if request.method == "POST":
currentSetting = models.UserSetting.objects.filter(isCurrent=True)
if currentSetting.serialPort:
print("GOT IT!")
a = {'data':'success'}
return HttpResponse(json.dumps(a))
This will return a dictionary required by the ajax.

Ajax POST with csrfmiddlewaretoken and csrftoken cookie set still gets django 403 Forbidden

I've read that both the csrfmiddlewaretoken and the csrftoken cookie have to be the correct value for a django POST request to succeed (django: csrftoken COOKIE vs. csrfmiddlewaretoken HTML Form value). This is the case for me yet I still get a 403:
In the chrome console:
document.cookie
returns
"csrftoken=Wt9eeJop5Vb3OmeNTvogegckm1pVM5MD"
but
$.get('https://learningdollars.fwd.wf/csrftoken/', function(data){
console.log(data)
token = $.parseHTML(data)[0].value
console.log(token)
$.ajax({
type: "POST",
url: 'https://learningdollars.fwd.wf/confirmemail/',
data: {email: 'gdasu#alumni.stanford.edu', csrfmiddlewaretoken: token},
contentType: "application/json"
})
.done(function(response) {
console.log('done!')
})
.fail(function(error) {
console.log('fail!')
})
})
returns
> Object {readyState: 1, getResponseHeader: function, getAllResponseHeaders: function, setRequestHeader: function, overrideMimeType: function…}
> <input type='hidden' name='csrfmiddlewaretoken' value='Wt9eeJop5Vb3OmeNTvogegckm1pVM5MD' />
> Wt9eeJop5Vb3OmeNTvogegckm1pVM5MD
> POST https://learningdollars.fwd.wf/confirmemail/ 403 (Forbidden)
> fail!
I have
'django.middleware.csrf.CsrfViewMiddleware',
activated in my django middleware.
My root urls.py contains:
url(r'^csrftoken/$', views.get_csrf_token),
url(r'^confirmemail/$', views.confirm_email, name='confirm_email'),
And, my views are:
def get_csrf_token(request):
c = {}
c.update(csrf(request))
print c
return render_to_response('csrf.html', c)
def confirm_email(request):
print 'here'
return JsonResponse({'response': 0})
And by the way, the contents of csrf.html are just the csrftoken (inside an input tag):
{% csrf_token %}
What am I doing wrong?
Well, I found a solution. It was just sending in the data as a URI rather than as json. (I by the way tried specifying dataType: 'json' in the above to no avail.) The following is in the chrome console:
> email = 'gdasu#alumni.stanford.edu'
< "gdasu#alumni.stanford.edu"
> csrftoken = 'L2MxD1XQIF1Xto5NkzUgGUYiHPyyz3K5'
< "L2MxD1XQIF1Xto5NkzUgGUYiHPyyz3K5"
> $.ajax({
type: "POST",
url: 'https://learningdollars.fwd.wf/confirmemail/',
data: "email="+ encodeURI(email) + "&csrfmiddlewaretoken=" + encodeURI(csrftoken),
success: function(data) { console.log(data); }
})
< Object {response: 1}
Still unsure what I was doing wrong on the json side, but the following is what I tried:
$.ajax({
type: "POST",
url: "https://learningdollars.fwd.wf/confirmemail/",
data: JSON.stringify({ email: email, csrfmiddlewaretoken: csrftoken }),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(data){
alert(data);
},
failure: function(errMsg) {
alert(errMsg);
}
});
You can forbid Django CSRF by adding csrf_exempt to tell the view not check csrf token.
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def get_csrf_token(request):
c = {}
c.update(csrf(request))
print c
return render_to_response('csrf.html', c)
#csrf_exempt
def confirm_email(request):
print 'here'
return JsonResponse({'response': 0})

Django, jquery, and modelforms

Looking to get some insight and tips on using ajax with django.
Say I have a function:
def add_comment(request, pk):
if request.method == 'POST' and request.is_ajax():
comment_form = CommentForm(request.POST)
if comment_form.is_valid():
comment = comment_form.save(commit=True)
comment.save()
json = simplejson.dumps(comment, ensure_ascii=False)
return HttpResponse(json, mimetype='application/json')
return render_to_response({{ post.id }}', {'comment': comment,}), context_instance=RequestContext(request), mimetype='application/json')
and I'm trying to post the comments to a page without a redirect with ajax function:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script></javascript>
<script type="text/javascript">
$(document).click(function()
{
$('#comment_form').submit(function()
{
var dataString = $('#comment_form').serialize();
$.ajax({
type: 'POST',
url: '',
data: dataString,
success: function(data){
$('').html(data);
},
});
return false;
});
});
</script>
I believe I'm mixing up a few things here. I am trying to get the page to load comments without a redirect. I don't need an exact answer, maybe just steered in the right direction.
This can helps:
this could be your view:
import json
def add_comment(request, pk):
if request.method == 'POST' and request.is_ajax():
comment_form = CommentForm(request.POST)
if comment_form.is_valid():
comment = comment_form.save(commit=True)
comment.save()
json_response = json.dumps({"status":"Success"})
return HttpResponse(json_response)
errors = {}
for k, v in job_type_form.errors.items():
errors[k.capitalize()] = v
response = {
'success': False,
'errors': errors
}
return HttpResponse(json.dumps(response))
and your jquery could be like this:
$('#comment_form').submit(function() {
var dataString = $('#comment_form').serialize();
$.ajax({
type: 'POST',
url: '',// you need to put this to something like '{% url to_your_view %}'
data: dataString,
dataType: 'json'
success: function(data){
// you can access to your json object like data.status or data.something
$('').html(data.status);
},
});
return false;
});
Thanks for the posts I finally got things worked out. The jquery was the main issue.
$(document).ready(function() {
$('#comment_form').submit(function(e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: '{% url art.views.post %}',
data: $('#comment_form').serialize(),
dataType: 'json';
success: function(){
location.reload();
$('#comment_form').get(0).reset();
},
});
return false;
});
});
I was sending the DOM object not the actual form data to the view.
In the view I combined two functions to get the two sharing the same URL.
def post(request, pk):
post = Post.objects.get.(pk=int(pk))
comments = Comment.objects.filter(post=post)
_dict = dict(post=post, comments=comments, form=Comment_form(), user=request.user)
_dict.update(csrf(request))
cf_obj = Comment(post = Post.objects.get(pk=pk))
if request.method == 'POST' and request.is_ajax():
if comment_form.is_valid():
comment = comment_form.save(commit=True)
else:
raise Http404
response = serializers.serialize('json', [comment])
return HttpResponse(response, mimetype='application/json')
return render_to_response('post.html', d)

Resources