I\'m trying to send my comment form using ajax, right now when user inserts a comment then whole page gets refreshed. I want this to be inserted nicely without page getting
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
class CommentForm(forms.ModelForm):
class Meta:
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
class CommentFormView(CreateView):
form_class = CommentForm
class CommentAPISubmitView(APIView):
def post(self, request, format=None):
#... Your checks goes here ...
form = CommentForm(request.POST or None)
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()
});
});
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.
In your javascript, you call for the class .commentForAjax. You should call the ID:
$(document).on('submit','#commentForAjax', function(e){
// ...
});
Hope this helps. R
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 url
data:$(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
def comment_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 = None
if parent_id is not None:
try:
parent_comment = Comment.objects.get(id=parent_id)
except:
parent_comment = None
if parent_comment is not None and parent_comment.post is not None:
post = parent_comment.post
form = CommentForm(request.POST)
if form.is_valid():
comment_text = form.cleaned_data['comment']
if parent_comment is not None:
# 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.
This is the easiest example of how to implement Ajax forms in conjunction with Django:
You can use Ajax Post to send JSON
to Django and then handle the arguments as a dict()
. Here is an example:
In browser (JQuery/JavaScript):
function newModule() {
var my_data = $("#my_element").val(); // Whatever value you want to be sent.
$.ajax({
url: "{% url 'modules' %}", // Handler as defined in Django URLs.
type: "POST", // Method.
dataType: "json", // Format as JSON (Default).
data: {
path: my_data, // Dictionary key (JSON).
csrfmiddlewaretoken:
'{{ csrf_token }}' // Unique key.
},
success: function (json) {
// On success do this.
},
error: function (xhr, errmsg, err) {
// On failure do this.
}
});
In server engine (Python):
def handle(request):
# Post request containing the key.
if request.method == 'POST' and 'my_data' in request.POST.keys():
# Retrieving the value.
my_data = request.POST['my_data']
# ...
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 to JsonResponse
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.