How To Use Ajax Function To Send Form Without Page Getting Refreshed, What Am I Missing?do I Have To Use Rest-framework For This?
Solution 1:
see official document of submit() and serialize() and modify your ajax all like this :
<script>
$('#commentForAjax' ).submit(function(e){
e.preventDefault();
$.ajax({
type:'POST',
url:'comment/create/', // make sure , you are calling currect urldata:$(this).serialize(),
success:function(json){
alert(json.message);
if(json.status==200){
var comment = json.comment;
var user = json.user;
/// set `comment` and `user` using jquery to some element
}
},
error:function(response){
alert("some error occured. see console for detail");
}
});
});
At backend side you are returning HttpResponseRedirect()
which will redirect your ajax call to some url(status code=302). I will suggest to return any json response.
For Django 1.7+ add line from django.http import JsonResponse
to return json response
For pre Django 1.7 use return HttpResponse(json.dumps(response_data), content_type="application/json")
Modify this portion of your views.py to return Json response
defcomment_create_view(request):
# you are calling this url using post method if request.method == "POST"and request.user.is_authenticated():
parent_id = request.POST.get('parent_id')
post_id = request.POST.get("post_id")
origin_path = request.POST.get("origin_path")
try:
post = Post.objects.get(id=post_id)
except:
# you should return from here , if post does not exists
response = {"code":400,"message":"Post does not exists"}
return HttpResponse(json.dumps(response), content_type="application/json")
parent_comment = Noneif parent_id isnotNone:
try:
parent_comment = Comment.objects.get(id=parent_id)
except:
parent_comment = Noneif parent_comment isnotNoneand parent_comment.post isnotNone:
post = parent_comment.post
form = CommentForm(request.POST)
if form.is_valid():
comment_text = form.cleaned_data['comment']
if parent_comment isnotNone:
# parent comments exists
new_comment = Comment.objects.create_comment(
user=MyProfile.objects.get(user=request.user),
path=parent_comment.get_origin,
text=comment_text,
post = post,
parent=parent_comment
)
response = {"status":200,"message":"comment_stored",
"user":new_comment.user,
"comment":comment_text,
}
return HttpResponse(json.dumps(response), content_type="application/json")
else:
new_comment = Comment.objects.create_comment(
user=MyProfile.objects.get(user=request.user),
path=origin_path,
text=comment_text,
post = post
)
response = {"status":200,"message":"new comment_stored",
"user":new_comment.user,
"comment":comment_text,}
return HttpResponse(json.dumps(response), content_type="application/json")
else:
messages.error(request, "There was an error with your comment.")
response = {"status":400,"message":"There was an error with your comment."}
return HttpResponse(json.dumps(response), content_type="application/json")
You don't have to use rest-framework. But if you use rest-framework for this purpose , it will be easy to implement.
Solution 2:
This is general structure of your comment app. I am assuming you are using Django REST Framework
- Comment
- models.py
- forms.py
- views.py
Comment Model (models.py)
from django.db import models class Comment(models.Model): user = models.ForeignKey(MyProfile) post = models.ForeignKey(Post) parent = models.ForeignKey("self") text = models.TextField() path = ... ...
Comment Form (forms.py)
from django import forms from .models import Comment classCommentForm(forms.ModelForm): classMeta: model = Comment fields = ('text', 'post_id', 'parent_id') post_id = forms.HiddenInput() parent_id = forms.HiddenInput() def__init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['text'].label = _('Comment') self.fields['post_id'].value = self.instance.post.id self.fields['parent_id'].value = self.instance.parent.id self.helper = FormHelper() self.helper.form_show_labels = False self.helper.layout = Layout( ManageFieldsWrapper( crispy_fields.Field('text') ), crispy_fields.SubmitField(submit_label='Leave your thoughts') )
Comment form view and api view (views.py)
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from django.http import HttpResponseRedirect from django.views.generic.edit import CreateView from .forms import CommentForm from .models import Comment classCommentFormView(CreateView): form_class = CommentForm classCommentAPISubmitView(APIView): defpost(self, request, format=None): #... Your checks goes here ... form = CommentForm(request.POST orNone) if form.is_valid(): #... Your saving logic here ..return HttpResponseRedirect(redirect_url) else: return HttpResponseRedirect(origin_path) return Response(status=status.HTTP_400_BAD_REQUEST)
Finally client side code AJAX/JQuery
$(document).ready(function() { $("#commentForAjax").submit(function( event ) { $.ajax({ type:'POST', url:'comment/create/', data:{ post_id:$('#post_id').val(), origin_path:$('#origin_path').val(), parent_id:$('#parent_id').val(), csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val() }, success: function(response){ } }); event.preventDefault() }); });
Solution 3:
As per your current code It seems like it will always get redirect because after validating the comment form and updating it into database you are returning HttpResponseRedirect
which is designed to redirect.
I think what you want is to update the comment into database and get a success response.
So to achieve this you need to change the response type, I would suggest return JsonResponse and based on the json response you can update the html as well, I am sure returning json response won't cause redirect your html.
Let me know if it's clear to you.
Thanks.
Solution 4:
After completely reviewing your code and discussing with OP in length. I've managed to resolve the OPs issue.
After removing
HttpResponseRedirect
I first converted it toJsonResponse
and made changes accordingly.response_data = { "status":200, "message":"comment_stored", "parent": True, "parent_id": parent_comment.id, "comment_count": parent_comment.comment_count() } return JsonResponse(response_data)
Next step was to simply perform DOM manipulation to display the data fetched from the response. But turns out this was more complicated than expected. So, to simplify it I simply separated the template into 2 parts - one will be the main part and the other containing the HTML of only the comment.
Using
django.template.loader.render_to_string
I generated the required HTML to display the comment and sent with the response as a string in JSON.html = render_to_string('main/child_comment.html', {'comment': [new_comment], 'user': request.user, 'comment_form':comment_form }) response_data = { "status":200, "message":"comment_stored", "comment":html, "parent": True, "parent_id": parent_comment.id, "comment_count": parent_comment.comment_count() } return JsonResponse(response_data)
Finally, after minor changes (not relevant to the current issue) mainly in the DOM manipulation scripts and in one of the form models, the code worked as expected.
$('form').submit(function(e) { e.preventDefault(); if ($(this).parents("tr") != 0) { parent_id = $(this).parents("tr").attr("id").split("_")[1]; data_str = $(this).serialize() + "&parent_id=" + parent_id; } else { data_str = $(this).serialize(); } $(this).parents("tr").attr("id").split("_")[1] $.ajax({ type: 'POST', url: '{% url 'comment_create' %}', data: data_str, success: function(json) { alert(json.message); if (json.status == 200) { var comment = json.comment.trim(); var user = json.user; if (!json.parent) { $(comment).insertBefore('.table tr:first'); } else { $(comment).insertBefore('#comment_' + json.parent_id + ' #child_comment:first'); $(".replies").text("답글" + json.comment_count + "개 모두 보기"); } } }, error: function(response) { alert("some error occured. see console for detail"); } }); });
BONUS: There was another minor issue which we had faced but I won't discuss it here. I've written a separate answer for it.
Solution 5:
The main thing you need to do for preventing page reloading on form submit is calling event.preventDefault()
in your form submit
event handler.
@Vibhu provided a very good code example in the answer above. That's exactly what you need to do on client side. (I provide his code with a single difference, request is sent to 'comment/create-ajax', not to 'comment/create'
$(document).ready(function() {
$("#commentForAjax").submit(function( event ) {
$.ajax({
type:'POST',
url:'comment/create-ajax',
data:{
post_id:$('#post_id').val(),
origin_path:$('#origin_path').val(),
parent_id:$('#parent_id').val(),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val()
},
success: function(response){
}
});
event.preventDefault()
});
});
On server side, you may provide one more controller (view in Django terms) which handles POST /comments/create-ajax
, it should look somehow like this:
defhandle_comment_creation_via_ajax(request):
comment_dict = json.loads(request.body)
# or json.loads(request.body.decode("utf-8")) if you use python 3
comment = CommentModel(**comment_dict) # This will work if comment has no nested models, otherwise, implement static method in your model class which takes dict and returns model instance.try:
comment.save()
except Exception as e:
return JsonResponse({'success':false, 'message': str(e)}, status_code=400)
return JsonResponse(model_to_dict(instance, fields=[], exclude=[])
JsonResponse and model_to_dict
Good luck!
P.S. Note that incoming model must be validated before save
Post a Comment for "How To Use Ajax Function To Send Form Without Page Getting Refreshed, What Am I Missing?do I Have To Use Rest-framework For This?"