I started with the following code to send the ID to a method to delete an object of model.But the first ID is always sent.
this is my HTML and JavaScript code:
{% for yad in yads %}
<form action="{% url 'del-yadd' %}" method="post" id="myform">
{% csrf_token %}
<button type="button" name="yidd" value="{{yad.id}}" id="btndelme" class=" btn btn-danger" onclick="
swal({
title: 'warning!',
text: 'do you want to delete?',
type: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
cancelButtonText:'no',
confirmButtonText: 'yes'
},
function(isConfirm) {
if (isConfirm) {
document.querySelector('#myform').submit();
}});
">delete</button>
</form>
{% endfor %}
and this is my Django view code:
def delete_yadd(request):
id=request.POST['yidd']
yad=Yadd.objects.filter(id=id).delete()
return redirect('home')
The reason why your SweetAlert is not showing is because it needs to be called right near the end of the html document by using a javascript function. Things I have changed are the way you call the sweetalert function like below:
{% for yad in yads %}
<form action="{% url 'del-yadd' %}" method="post" id="myform">
{% csrf_token %}
<button type="button" name="yidd" value="{{yad.id}}" id="btndelme" class=" btn btn-danger" onclick="confirm()"></button>
</form>
{% endfor %}
<script>
function confirm() {
swal({
title: 'warning!',
text: 'do you want to delete?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
cancelButtonText: 'no',
confirmButtonText: 'yes'
},
function (isConfirm) {
if (isConfirm) {
document.querySelector('#myform').submit();
}
})
}
</script>
Related
I am trying to clear all filters on button click at once. This is what I have on filters.py file and filters class:
class Filters(django_filters.FilterSet):
id = django_filters.NumberFilter(label=_("ID"))
name = django_filters.TextFilter(label=_("Name"))
And in base template:
<form id="filters-filters-form" action="javascript:;" onsubmit="onSubmit(this)" class="form form-inline main-filter">
{% bootstrap_form filter.form layout='inline' %}
<div>
<button class="btn ml-auto mr-2" onclick="resetFilters()">Clear all</button>
{% trans "Apply" as button_text %}
{% bootstrap_button button_text button_class="btn-primary" %}
</div>
</form>
resetFilters() function:
var resetFilters = function() {
let formId = document.getElementById('filters-form')
let formChildren = formId.childNodes;
Array.from(formChildren).forEach(formChild => {
formChild.val(null).trigger('change')
});
}
Is there any easy way to reset all filters?
P.S: I need to reset these filters without any id of form-control because it will be reusable base template
you can just add a button and add the URL of your form :
<a class="btn ml-auto mr-2" href={<pass your URL here>} > Clear All </a>
On site exists form where users able to comment products. Comments are connected with products. jQuery is used from Bootstrap base.html (it's worked with another ajax-call). I'm fighting with ajax comment during the week). Trying to do it only for one product to understand how it works. Without ajax system of comments works fine, but I decided to add smooth in comment add without refreshing of the page and I do task with POST and ajax for the first time (before I did two pretty simple examples with get and form reset after successful response to ajax from back-end). Could somebody advice what should be add in template and view ? I suppose problem is connected with my poor knowledge of js.
I am using
is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
because this is working with django 4.0, and old function is_ajax() was removed.
views.py
def ajax_test(request):
product = Product.objects.get(id=4)
comments = product.comment_set.order_by('-created_at')
form = UserCommentForm
context = {'product':product,'comments':comments,'form': form}
return render(request, 'store/ajax_test.html', context)
def ajax_receiver(request):
is_ajax = request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
if request.method == 'POST' and is_ajax:
product = Product.objects.get(id=4)
form = UserCommentForm(data=request.POST)
user = Comment.objects.get(author=user)
if form.is_valid() and request.user.is_authenticated:
new_comment = form.save(commit=False)
new_comment.author = request.user
new_comment.product = product
new_comment.save()
comment_info = {
"author": new_comment.author,
"new_comment": new_comment.content,
"created_at": new_comment.created_at,
}
return JsonResponse({"comment_info": comment_info}, status=200)
else:
return JsonResponse({"success": False}, status=400)
template
{% extends "store/base.html" %}
{% load bootstrap4 %}
{% block page_header %}
<h1>тестирую аякс</h1>
{% endblock page_header %}
{% block content %}
<div class="container">
<div class="row py-3">
<div class="col-3 order-2" id="sticky-sidebar">
<div class="sticky-top">
<form id="UserCommentForm" method='post' class="form">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button name="submit" class="btn btn-primary" name="comment">Добавить комментарий</button>
{% endbuttons %}
</form>
</div>
</div>
<div class="col" id="main">
<h3>Комментарии:</h3>
{% 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 action="" method="POST">
{% csrf_token %}
</form>
</div>
{% empty %}
<p>Для данного товара ещё нет комментариев.</p>
{% endfor %}
</div>
</div>
</div>
{% block javascript %}
<script type="text/javascript">
$(document).ready(function(){
$("#UserCommentForm").submit(function(e) {
// prevent from normal form behaviour
e.preventDefault();
// serialize the form data
var serializedData = $(this).serialize();
$.ajax({
type : 'POST',
url : "{% url 'store:ajax_receiver' %}",
data : serializedData,
success : function(response){
??? what should be there to post ???
},
error : function(response){
console.log(response)
}
});
});
});
</script>
{% endblock javascript %}
{% endblock content %}
Finally I found the decision, it's just to reload block of comments without refreshing, there is the code, the space before ' #main' is important!:
<script type="text/javascript">
$(document).ready(function(){
$("#UserCommentForm").submit(function(e){
// prevent from normal form behaviour
e.preventDefault();
// serialize the form data
var serializedData = $(this).serialize();
$.ajax({
type : 'POST',
url : "{% url 'store:ajax_receiver' %}",
data : serializedData,
success : function(response){
$('#main').load(' #main', function(){
/// can add another function here
});
$("#UserCommentForm")[0].reset();
},
error : function(response){
console.log(response)
}
});
});
});
</script>
In your ajax success write the following code to show new comment:
let comment_info = response["comment_info"];
let author = comment_info["author"];
let comment = comment_info["comment"];
let created_at = comment_info["created_at"];
html = "<h6 class='card-header'> "+author+"</h6>"
// i have writting only part of html write the full just like you do when you show comments
$("#main").append(html);
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/
My application currently flows through 3 pages:
User selects question in index page
User submits answer in answer page
User is presented with result in results page.
I want to compress that down to a single page where the user submits an answer to the question and result is shown on the same page.
The following django-template code separates questions with Bootstrap accordion. How do I post the form without refreshing the whole page? I want to be able to display the result on the page, update CSS styling with Javascript etc.
<h2>{{ category.title }}</h2>
<div class="accordion" id="accordion{{category.title}}">
{% for challenge in category.challenge_set.all %}
<div class="card">
<div class="card-header" id="heading{{challenge.id}}">
<h2 class="mb-0">
<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapse{{challenge.id}}" aria-expanded="true" aria-controls="collapse{{challenge.id}}">
{{ challenge.question_text }} - {{ challenge.point_value }} points
</button>
</h2>
</div>
<div id="collapse{{challenge.id}}" class="collapse in" aria-labelledby="heading{{challenge.id}}" data-parent="#accordion{{category.title}}">
<div class="card-body">
<p>{{ challenge.description }}</p>
<form action="{% url 'challenges:answer' challenge.id %}" method="post">
{% if challenge|is_answered:request %}
<label for="answered">Answer</label>
<input type="text" name="answered" id="answered" value="{{ challenge.answer_text }}" readonly>
{% else %}
{% csrf_token %}
<label for="answer">Answer</label>
<input type="text" name="answer" id="answer">
<input type="submit" value="Submit">
{% endif %}
</form>
</div>
</div>
{% endfor %}
</div>
Here is the view:
def index(request):
context = {'challenges_by_category_list': Category.objects.all()}
return render(request, 'challenges/index.html', context)
def detail(request, challenge_id):
challenge = get_object_or_404(Challenge, pk=challenge_id)
return render(request, 'challenges/detail.html', {'challenge': challenge})
def results(request, challenge_id, result):
challenge = get_object_or_404(Challenge, pk=challenge_id)
return render(request, 'challenges/results.html', {'challenge':challenge, 'result':result})
def answer(request, challenge_id):
challenge = get_object_or_404(Challenge, pk=challenge_id)
result = "Incorrect, try again!"
if challenge.answer_text.lower() == request.POST['answer'].lower():
current_user = request.user
session = User_Challenge(user=current_user, challenge=challenge, answered=True)
session.save()
points = Profile(user=current_user, points=challenge.point_value)
points.save()
result = "Correct!"
return HttpResponseRedirect(reverse('challenges:results', args=(challenge.id, result)))
You can try this:
Add the below script in your template:
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
write a script and a function inside it to submit the form data.
<script type="text/javascript">
function submitData( challenge_id ){
// Get answer from the input element
var answer = document.getElementById("answer").value;
// add the url over here where you want to submit form & challenge_id is also taken as a parameter.
var url = "<your_url>";
$.ajax({
url: url,
data: {
'answer': answer,
},
dataType: 'JSON',
success: function(data){
// show an alert message when form is submitted and it gets a response from the view where result is provided and if url is provided then redirect the user to that url.
alert(data.result);
if (data.url){
window.open(data.url, '_self');
}
}
});
}
</script>
Change type of the submit button and add an onclick event to call the submitData() function and pass the challenge id to it. And remove the action attr from the form.
see below:
<form method="post">
{% csrf_token %}
{% if challenge|is_answered:request %}
<label for="answered">Answer</label>
<input type="text" name="answered" id="answered" value="{{ challenge.answer_text }}" readonly>
{% else %}
<label for="answer">Answer</label>
<input type="text" name="answer" id="answer">
// over here
<button type="button" onclick="submitData({{ challenge.id }})">
Submit
</button>
{% endif %}
</form>
Return a JsonReponse to the ajax call from the views.
views.py
def answer(request, challenge_id):
answer = request.GET.get('answer', False)
url = False
if challenge.objects.filter(id=challenge_id).exists() and answer:
challenge = Challenge.objects.get(id=challenge_id)
if challenge.answer_text.lower() == answer.lower():
current_user = request.user
session = User_Challenge(user=current_user, challenge=challenge, answered=True)
session.save()
points = Profile(user=current_user, points=challenge.point_value)
points.save()
result = "Correct!"
# specify the url where you want to redirect the user after correct answer
url = ""
else:
result = "Incorrect, try again!"
data = {
'result': result,
'url': url
}
return JsonResponse(data)
Description
I am creating a like button for a comment that was made on a post.
When the user clicks on the like button, Django gives a NoReverseMatch and the page doesn't update automatically.
After I refresh the page, it shows that the like was updated in the database, despite the NoReverseMatch error.
What I tried?
Initially, the issue was with the csrf_tokens. So, I added the csrf_exempt decorator, so it is definitely not the issue.
I played a lot with the urls, and tried adding the comment id to the parameter, but it doesn't seem to help.
Question
How can I resolve the NoReverseMatch error and make it so that AJAX automatically changes the button from like to dislike.
urls.py
path('post/<int:pk>/like-comment', like_comment, name="like_comment")
view.py
#csrf_exempt
def like_comment(request, pk):
comment = get_object_or_404(Comment, id=request.POST.get('id'))
liked = False
if comment.likes.filter(id=request.user.id).exists():
comment.likes.remove(request.user)
liked = False
else:
comment.likes.add(request.user)
liked = True
context = {
'comment': comment,
'is_liked': liked,
'total_likes': comment.likes.count()
}
if request.is_ajax():
html = render_to_string('blog/like_section_comment.html', context, request=request)
return JsonResponse({'form': html})
main.js
$(document).ready(function(event){
$(document).on('click', '#like-comment', function(event){
event.preventDefault();
var pk1 = $(this).attr('value'); // comment id
var pk = $(this).attr('data-value'); // post id
$.ajax({
type: 'POST',
url: "like-comment",
data: {'id': pk1, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: 'json',
success: function(response) {
$('#like-section-comment').html(response['form'])
console.log($('#like-section-comment').html(response['form']));
},
error: function(rs, e) {
console.log(rs.responseText);
},
});
});
});
comment_section.html
<article class="media content-section">
{% if user.is_authenticated %}
<form id="like-form-comment" method="post">
{% csrf_token %}
{% if request.user in comment.likes.all %}
<button type="submit" id="like-comment" name="comment_id" data-value="{{ comment.post.id }}" value="{{ comment.id }}" class="btn btn-danger btn-sm">
Dislike
</button>
{% else %}
<button type="submit" id="like-comment" name="comment_id" data-value="{{ comment.post.id }}" value="{{ comment.id }}" class="btn btn-primary btn-sm">
Like
</button>
{% endif %}
</form>
{% endif %}
You write url: "like-comment", how would that map to the correct url? Javascript does not know anything about Django. Try this instead:
In your html:
<button type="submit" id="like-comment" name="comment_id" data-value="{{ comment.post.id }}" value="{{ comment.id }}" data-submit-url="{% url 'like_comment' comment.id %}" class="btn btn-primary btn-sm">
Now in your JavaScript:
url: $(this).attr('data-submit-url'),