I want to implement a ajax commenting system.
urls.py:
(r'^comment/(\d+)/$', comments),
views.py:
def comments(request,feed):
if request.method == 'POST':
feed=Feed.objects.get(pk=feed)
form = CommentForm(request.POST)
if form.is_valid():
comment, created = Comment.objects.get_or_create(
feed=feed,
msg=form.cleaned_data['msg'],
ip=request.META['REMOTE_ADDR']
)
comments=Comment.objects.filter(feed=feed)
form=CommentForm()
variables=RequestContext(request,{'comments': comments,'feed': feed,'form':form,})
if 'HTTP_REFERER' in request.META:
return HttpResponseRedirect(request.META['HTTP_REFERER'])
return render_to_response('comment_page.html', variables )
#return HttpResponseRedirect('/view/')
else:
form=CommentForm()
feed=Feed.objects.get(pk=feed)
comments=Comment.objects.filter(feed=feed).reverse()
variables=RequestContext(request,{'comments': comments,'feed': feed,'form':form,})
return render_to_response('comment_page.html', variables )
Templates:
<div id="commentbox" style="display:none;">
<form class="comment" method="post" action="/comment/{{feed.id}}/">
{{cform.as_p}}
<input class="post" type="submit" value="post" />
</form>
</div>
</br>
<h3></h3><button class="ccc">Show/Hide Comment</button> {{feed.comment_set.count}} Comments
<div id="commentlist" class="commentlist" style="padding-left:10px;"><ul style="list-style-type:square;">
{% for c in feed.comment_set.all %}
<li>{{c.msg}}</li>
{% endfor %}
</ul>
</div>
What code should I include to add comments into commentlist li field without page refresh. I am new in ajax. Please help. Thanks
Here's what I would do:
Leave the HTML as it is, as it works for people without JavaScript. In you JavaScript, when the user submits the form, stop it from actually happening:
$('#commentbox form').submit(function(e) {
e.preventDefault();
});
Now, when the button is pressed, prevent the default behavior and submit the form via AJAX:
$('#commentbox form').submit(function(e) {
e.preventDefault();
$.ajax({
type: 'post',
url: $(this).parent().attr('action'),
data: $(this).parent().serialize(),
}).done(function(data) {
alert('The AJAX is done, and the server said ' + data);
});
});
Related
I would like to understand how to submit 2 forms simultaneously using ajax.
I display a form composed of 2 model forms, for 2 models linked with OneToOne relationship. Managing the display is not a big deal, I can also manage the submit using a Django view; but I would like to know how to deal with javascript.
Why? The form is called from a global menu and displayed in a modal, and I would like only to close the modal on submit, but not refresh the page.
Here are some (simplified) code snippets I tried so far:
user_profile.html (modal form):
<form id="upd-user" action="." method="post" url-endpoint="{% url 'polls:upd_user_detail' %}"
comp-slug="{{ comp_slug }}" usr-id="{% if usr_id %} {{ usr_id }} {% else %} 0 {% endif %}">
{% csrf_token %}
{{ user_form }}
{{ usercomp_form }}
<button class="btn btn-success" type="submit">{% if usr_id %}Mettre à jour{% else %}Créer{% endif %}</button>
     
<a class="btn btn-secondary back_btn" data-dismiss="modal">Annuler</a>
</form>
The modal is embedded in this snippet:
<div id="usr_detail" class="modal fade hide" role="dialog" tabindex='-1'>
<div class="modal-dialog modal-lg">
<div class="modal-content usr_form_content">
{% include './user_profile.html' %}
</div>
</div>
</div>
Javascript submit function:
$(".usr_form_content").on("submit", "#upd-user", function(event) {
event.preventDefault();
$.ajax({
method: 'POST',
url: $(this).attr('url-endpoint'),
data: {
comp_slug: $(this).attr('comp-slug'),
usr_id: $(this).attr('usr-id'),
user_form: user_form.serialize(),
usercomp_form: usercomp_form.serialize()
},
success: handleSuccess,
error: handleError,
});
function handleSuccess(data) {
$("#usr_detail").modal("hide");
};
function handleError(jqXHR, textStatus, errorThrown){
console.log(jqXHR);
console.log(textStatus);
console.log(errorThrown);
}
})
The error message is quite clear:
Uncaught ReferenceError: user_form is not defined
I now I need to make the transition between the form and js, I probably need to link these values to $('this) but I don't know how.
If it helps, here's the GET view:
comp_slug = request.GET["comp_slug"]
company = Company.get_company(comp_slug)
usr_id = int(request.GET["usr_id"]) # comes as string when 0
if usr_id > 0:
profile_user = User.objects.get(pk=usr_id)
user_form = UserBaseForm(instance=profile_user)
usercomp_form = UserCompForm(instance=profile_user.usercomp)
user_form.fields['username'].disabled = True # Disable updates of the field
else:
user_form = UserBaseForm()
usercomp_form = UserCompForm()
context = {
"comp_slug": comp_slug,
"company": company,
"usr_id": usr_id,
"user_form": user_form,
"usercomp_form": usercomp_form,
}
template = render_to_string('polls/user_profile.html', context=context, request=request)
return JsonResponse({"user_template": template})
And related js part (it works fine - I tried to generated necessary variables at this stage, but it did not work neither)
$('.update-user').click(function() {
$.ajax({
method: 'GET',
url: $(this).attr('url-endpoint'),
data: {
comp_slug: $(this).attr('comp-slug'),
usr_id: $(this).attr('usr-id'),
},
success: handleSuccess,
error: handleError,
});
function handleSuccess(data) {
$(".usr_form_content").html(data.user_template);
};
function handleError(error_data) {
console.log("error");
console.log(error_data);
};
})
I'm fighting with this problem during several days and can not find the solution for my case. I'm trying to make system of likes without refreshing the page. In synchronous mode system of likes and dislikes works fine, but when I'm trying to add AJAX, I'm getting 405 and only one last comment is working for one click, I understand problem that Ajax doesn't understand django urls with id or pk like my variant {% url 'store:like' comment.pk %} , but how it can be solved?
There is this part from the template:
{% for comment in comments %}
<h6 class="card-header">
{{ comment.author }}<small> добавлен {{ comment.created_at|date:'M d, Y H:i' }} </small>
</h6>
<div class="card-body">
<h4>{{ comment }}</h4>
<form id="like-{{comment.pk}}" method="POST" action="{% url 'store:add_like' comment.pk %}">
{% csrf_token %}
<button style="background-color: transparent; border: none; box-shadow: none;" type="submit">
<a class="btn btn-success" id="like-count-{{comment.pk}}"> Likes {{ comment.likes.all.count }}</a>
</button>
</form>
</div>
{% empty %}
<p>Для данного товара ещё нет комментариев.</p>
{% endfor %}
my ajax call in the same template:
<script type="text/javascript">
$(document).ready(function(){
$('[id^="like-"]').submit(function(e){
e.preventDefault();
var endpoint = $(this).attr('action');
var serializedData = $(this).serializeArray();
$.ajax({
type: 'POST',
url: endpoint,
data: serializedData,
success: function(response) {
$( "#like-count-"+response["id"].toString()).text("Likes "+response["like_count"]);
},
error: function(rs, e) {
alert(rs.responseText);
}
});
})
This part from urls:
path('products/<int:pk>/like/', addlike, name='like'),
View for like:
#api_view(['POST'])
def addlike(request, pk, *args, **kwargs):
is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
if request.method == 'POST' and is_ajax:
post = Comment.objects.get(pk=pk)
is_dislike = False
for dislike in post.dislikes.all():
if dislike == request.user:
is_dislike = True
break
if is_dislike:
post.dislikes.remove(request.user)
is_like = False
for like in post.likes.all():
if like == request.user:
is_like = True
break
if not is_like:
post.likes.add(request.user)
if is_like:
post.likes.remove(request.user)
all_post_likes = post.total_likes() (function from models)
return JsonResponse({"success": True, "like_count": all_post_likes, "id": pk}, status=200)
else:
return JsonResponse({"success": False}, status=400)
How to force AJAX to call in pk what I need? (Finally I found the solution, updated the final version of the code)
Remove the data-url and set the action attribute because when you click the submit button, by default this POST request will be sent to the current URL and you will receive the 405 status code, but if you set action this POST request will be sent to the like url:
<form id="like" method="POST" action="{% url 'store:like' comment.pk %}">
{% csrf_token %}
<input type="hidden" value="{{comment.pk}}" name="id">
<input type="hidden" name="next" value="{{ request.path }}">
<button style="background-color: transparent; border: none; box-shadow: none;" type="submit">
<a class="btn btn-success" id="like"> Likes {{ comment.likes.all.count }}</a>
</button>
</form>
And in js you can get the URL like this
var endpoint = $(this).attr('action');
Update
All forms have the same "ID" and therefore only the first one is executed and the rest of the forms are not managed by Ajax, to solve this problem you can give different ID to the forms(This condition also applies to a tags):
<form id="like-{{comment.pk}}" method="POST" action="{% url 'store:like' comment.pk %}">
{% csrf_token %}
........
.....
<a class="btn btn-success" id="likes-count-{{comment.pk}}"> Likes {{ comment.likes.all.count }}</a>
And you can receive events this way and update the number of likes.
$(document).ready(function(){
$('[id^="like-"]').submit(function(e){
e.preventDefault();
var endpoint = $(this).attr('action');
var serializedData = $(this).serializeArray();
$.ajax({
url: endpoint,
method: "POST",
data: serializedData,
success: function(response){
$("#likes-count-" + serializedData[1].value).text("Likes "+response["like_count"]);
}
});
})
});
in the view you should return the new number of the likes :
return JsonResponse({"success": True, "like_count": new_like_count}, status=200)
And in the success function you can access to the new number by the response and now we change the text value of a tag to change the like count number.
^= means: selects elements that have the specified attribute with a
value beginning exactly with a given string. attributeStartsWith1
I think you should send the csrf_token with the ajax request add
headers: { "X-CSRFToken": token } to your ajax request, "token is the csrf_token" or add #csrf_exempt decorator to your function but it will keep your view unsafe against CSRF attacks.
you can find more info here https://docs.djangoproject.com/en/4.0/ref/csrf/
when I post data to the database using ajax code I got an unexpected result. I can post data but output showing is wrong. My ajax code not execute properly
views.py
def notification(request):
user = request.user
if request.method == 'POST':
property_id = request.POST['property_id']
owner = request.POST['owner_id']
property_object = Property.objects.get(id=property_id)
owner_object =User.objects.all().get(username=owner)
notification = "Hi {}, {} \n have interested
in your property '{}'".format(property_object.owner, user.first_name, property_object.headline)
property_object.notify.add(user)
notifications = Notifications.objects.create(notification=notification, property=property_object, owner=owner_object)
notifications.save()
return JsonResponse({"msg":"success"})
ajax code
<form action="{% url 'notification' %}" method="post" id="notify_form" >
{% csrf_token %}
<input type="hidden" name="owner_id" value="{{ accomodation.owner }}">
<button type="buttonsn" id="request-btn" name="property_id" value="{{ accomodation.id }}" class="btn btn-primary">Contact info:</button>
</form>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
$(document).ready(function(e){
$(document).on('submit','#request-btn',function(e){
e.preventDefault();
var property_id = $this.attr('value');
var owner_id = $this.attr('value');
$.ajax({
type:'POST',
url:"{% url 'notification' %}",
data:$('#notify_form').serialize(),
dataType:'Json',
success:function(data){
if(data.msg == "success"){
alert('data submitted')
}
}
});
});
});
I'm expecting a success alert box, but got
{"msg": "success"}
in a white window like 'httpResponse'. ajax code doesn't work
for overcome i'am try somechanges in mycode
i removed action address from form
<form action="" method="post" id="notify_form" >
but it doesn't work this time form not submitted
what can I do
You're attaching the event handler to the button:
$(document).on('submit','#request-btn',function(e){
But buttons don't have submit events, forms do. You can attach it to the form:
$(document).on('submit','#notify_form',function(e){
Or use the button's click event instead:
$(document).on('click','#request-btn',function(e){
Note also that there's a typo in the type attribute for the <button> element. It should be:
type="button"
In detail.html:
<form id="answer_form" class="form-horizontal" action="{% url 'puzzle:update' puzzle.id %}" method="POST" onSubmit="return ValidateAnswer();">
{% csrf_token %}
<p>Please entry your answer below: (Case insensitive)</p>
<div class="form-group col-xs-12">
<input id="player_answer" maxlength="30" name="player_answer" type="text">
</div>
<div class="form-group col-xs-12">
<button id="submit_answer_btn" class="btn btn-success" type="submit">Submit</button>
</div>
</form>
<script>
function ValidateAnswer() {
var player_answer = document.getElementById("player_answer");
if(player_answer.value == {{ puzzle.answer }}) {
alert("Congratulations!");
} else {
alert("Wrong answer!");
return false;
}
}
</script>
In url.py
app_name = 'puzzle'
urlpatterns = [
url(r'^(?P<pk>[0-9]+)/$', login_required(views.PuzzleDetailView.as_view()), name='detail'),
url(r'^(?P<puzzle_id>[0-9]+)/update/$', views.update_user_game_history, name='update'),
]
In views.py
class PuzzleDetailView(DetailView):
model = Puzzle
template_name = 'puzzle/detail.html'
def update_user_game_history(request, puzzle_id):
player_game_history = PlayerGameHistory.objects.get(user=request.user)
solved_puzzle = Puzzle.objects.get(id=puzzle_id)
player_game_history.score += solved_puzzle.point
player_game_history.save()
return HttpResponseRedirect('/')
What I am trying to do is to click on Submit button, via 2nd url, go to the update_user_game_history function in views.py.
However, everytime I submit, the flow tries to, via 1st url, go to the PuzzleDetailView. And I get Method Not Allowed (POST): /2/ in terminal
I am not sure if that solved your problem but basically in the view you don't have a way to ensure if the method is 'POST' or not. To solve that you can add this line in your code:
def update_user_game_history(request, puzzle_id):
if request.method == 'POST':
player_game_history = PlayerGameHistory.objects.get(user=request.user)
solved_puzzle = Puzzle.objects.get(id=puzzle_id)
player_game_history.score += solved_puzzle.point
player_game_history.save()
return HttpResponseRedirect('/')
But according with your example is not necessary call a form because you aren't using it. My recommendation is to improve this view because only with a url you can score many times you want
Many thanks to Alasdair. After checking the rendered form tag, I found out the problem. Value of action attribute in form tag was wrong. It was still "#". It seems like Chrome back button doesn't retrieve the lastest page from server. It only reload what it received before.
I am quite new to django and struggling to do something very simple.
I have a ModelForm for the following model:
class Queries(models.Model):
user_id=models.CharField(max_length=200)
query=models.CharField(max_length=200)
And I am showing the user a simple form that will help in doing the following:
user will ask a question
The question will be processed(a database query will be generated
based on the question)
Then the query result should be shown just beneath the form in the
same page.
This is how my views.py looks like:
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render
from basicapp.models import QueryForm
def index(request):
form=MyForm()
real_form=form.getForm(request)
response=form.response
return render(request,'basicapp/index.html',{
'form': real_form,
'response':response,
})
class MyForm:
response=''
def getForm(self,request):
form = QueryForm(request.POST)
if form.is_valid():
response=form.cleaned_data['query']
form.save()
return form
For now I am trying simple stuffs,I am taking the value in query field of the form and trying to send it back to the page;so far I am failed.
This is index.html:
<form action=" " method="post">{% csrf_token %}
{{ form }}
<p>{{response}}</p>
<input type="submit" value="Submit" />
</form>
If I could do this,I think the query stuffs wont be that tough.The form is working fine,the datas are getting saved in database. Only the response string from views.py could not be retrieved inside index.html after form submission. Can you please help?
EDIT:
Tried following in index.html based on Hoff's answer:
<form id="myForm" action=" " method="get">{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
<div id="response">
</div>
<script language="JavaScript">
$(document).ready(function() {
$("#myForm").submit(function() { // catch the form's submit event
$.ajax({ // create an AJAX call...
data: $(this).serialize(), // get the form data
type: $(this).attr('GET'),
success: function(response) { // on success..
$("#response").html(response); // update the DIV
}
});
return false;
});
});
</script>
Still no luck :(
views.py
def index(request):
questions=None
if request.GET.get('search'):
search = request.GET.get('search')
questions = Queries.objects.filter(query__icontains=search)
name = request.GET.get('name')
query = Queries.object.create(query=search, user_id=name)
query.save()
return render(request, 'basicapp/index.html',{
'questions': questions,
})
html
<form method="GET">
Question: <input type="text" name="search"><br/>
Name: <input type="text" name="name"><br/>
<input type="submit" value="Submit" />
</form><br/><br/>
{% for question in questions %}
<p>{{question}}</p>
{% endfor %}
What you need is an asynchronous post (ajax), which is easy with jQuery, see this answer for a complete solution: How to POST a django form with AJAX & jQuery
<input type="text" name="query" />
<input type="submit" name="submit" value="Submit" />
you can check if the form was submitted or not (i.e if it's a post request or not):
if 'submit' in request.POST: #you could use 'query' instead of 'submit' too
# do post related task
# add context variables to render post output
# add another context variable to indicate if it's a post
# Example:
context.update({'post_output': request.POST.get('query','')})
...
return render(request, 'index.html', context)
Then in the template, check if context variable post_output exists, if it does show the output:
{% if post_output %}
Output: {{ post_output }}
{% endif %}
In short, the logic is:
Check if a relevant request.POST dict key exists or not in your view.
If the key exists, then it's a post request; add post related context variables and do post related tasks.
Check if any post related context variable is available in the template and if it does, show post related output.
If you don't want to show the output when the page is simply refreshed after a post, pass the request object to the template and do a check like this:
{% if request.POST.submit and post_output %}
Following Hoff's answer...
Add URL attribute to ajax call:
$(document).ready(function() {
$("#myForm").submit(function() { // catch the form's submit event
$.ajax({ // create an AJAX call...
data: $(this).serialize(), // get the form data
type: $(this).attr('GET'),
url: '/URL-to-ajax-view/',
success: function(response) { // on success..
$("#response").html(response); // update the DIV
}
});
return false;
});
});
Some ajax handler in views.py:
# /URL-to-ajax-view/
def ajax_get_response(request):
if request.method == "GET" and request.is_ajax:
form = QueryForm(request.POST or None)
if form.is_valid():
form.save()
return HttpResponse(form.response)
raise Http404
Tried something like that?