Building a like post in flask using ajax and jquery fails - javascript

I am building a simple flask blog and there I am trying to implement like functionality using ajax and jquery. It works without ajax but the page reloads. So I need to use the ajax but when I am using ajax then I got my site redirected to the JSON file which is not expected. Please Help me with this. The code is
<div class="likers" id = 'result{{ slide.id }}'>
{% if current_user.has_liked_slide(slide) %}
<a class="unlike" id="unlike_{{slide.id}}" href="{{ url_for('like_action', slide_id=slide.id, action='unlike') }}">unlike</a>
{% else %}
<a class="like" id="like_{{slide.id}}" href="{{ url_for('like_action', slide_id=slide.id, action='like') }}">like</a>
{% endif %}
<div id = 'like-count'>
{{ slide.likes | count }}
</div>
</div>
#app.route('/like/<int:slide_id>/<action>', methods = ['GET', 'POST'])
#login_required
def like_action(slide_id, action):
slide = Slide.query.filter_by(id=slide_id).first_or_404()
if action == 'like':
current_user.like_slide(slide)
db.session.commit()
return jsonify({'slide_id': slide_id, 'action': action})
if action == 'unlike':
current_user.unlike_slide(slide)
db.session.commit()
return jsonify({'slide_id': slide_id, 'action': action})
$(document).ready(function(event) {
$('.like, .unlike').click(function(){
var id = this.id;
var split_id = id.split('_');
var text = split_id[0];
var slide_id = split_id[1];
$.ajax({
url: '/like/<int:slide_id>/<action>',
type: 'post',
data : {slide_id:slide_id, action:text},
dataType:'html',
success: function(data){
$("like_" + slide_id).html(data);
$('unlikes_' + slide_id).html(data);
}
});
});
});

Related

Django + AJAX comments

I'm trying to implement a comment system without reloading the page, but it doesn't work. Comments are inserted only after the page is reloaded, but AJAX does not work.
<!-- COMMENT -->
<div class="comment-section">
<form action="{% url 'love:comment_urls' %}" method="POST" class='comment-form'>
{% csrf_token %}
<input type="hidden" name="post_id" value={{i.pk}}>
{{ comment_form }}
<button type="submit" name="submit_c_form">
Опубликовать
</button>
</form>
</div>
<hr>
<div class="comment_set">
{% if i.quotes_comment.all %}
{% for com in i.quotes_comment.all %}
<b>{{ com.user }}:</b>
{{ com.body }}
{% endfor %}
{% endif %}
</div>
</div>
JS code
// COMMENT
$(document).ready(function () {
$('.comment-form').submit(function () {
event.preventDefault();
console.log($(this).serialize());
var url = $(this).attr('action')
$.ajax({
type: 'POST',
url: url,
data: $(this).serialize(),
dataType: 'html',
success: function (response) {
console.log('Заработало')
$('.comment-section').html(response['form']);
},
error: function (rs, error) {
console.log(rs, error)
}
})
})
})
#csrf_exempt
def create_comment(request):
profile = Profile.objects.get(user=request.user)
data = json.loads(request.body)
post_id = data['post_id']
body = data['body']
comment_form = CommentModelForm(data)
if comment_form.is_valid():
print('Form is valid')
instance = comment_form.save(commit=False)
instance.user = profile
instance.quotes = QuotesHonors.objects.get(
id=post_id)
instance.save()
comment_form = CommentModelForm()
return JsonResponse({
'content': body
})
return redirect("love:home_urls")
As I said, comments are inserted only after a reboot, but when I click publish, nothing happens. If you change the dataType to 'json', the parser error pops up. Can you explain what I'm doing wrong?
Hard to say what you are doing wrong overall there is code missing. But from what you shared, csrf_token is not in AJAX request header. And mainly, by using JQuery .serialize(), you are trying to pass data in URL-encoded notation and not as a JSON object.
This .serialize() output would be used to pass data as a query string by appending it to a base URL. And, at the view accessing it with request.GET.get('parameter'). Instead, what we want is to pass data as JSON and access it through request.body at view level.
Here is a full example:
models.py
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
title = models.CharField(max_length=30)
body = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=timezone.now())
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='comments')
body = models.CharField(max_length=255)
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments', blank=True)
created_at = models.DateTimeField(auto_now_add=timezone.now())
urls.py
from .views import post_detail, comment_create
app_name = 'love'
urlpatterns = [
path('post/<int:pk>/', post_detail, name='post-detail'),
path('comment/create/', comment_create, name='comment-create'),
]
Two views, one to render the template and another to handle the AJAX request. The tricky part was trying to find a way to format date in the same way as the template.
views.py
from django.utils.formats import date_format
from django.shortcuts import get_object_or_404
from django.http import JsonResponse
import json
def post_detail(request, pk):
post = get_object_or_404(Post, id=pk)
has_commented = post.comments.filter(user=request.user).exists()
context = {
'post': post,
'has_commented': has_commented
}
return render(request, 'post_detail.html', context)
def comment_create(request):
data = json.loads(request.body)
post_id = data['post_id']
comment_body = data['body']
post = get_object_or_404(Post, id=post_id)
comment = Comment.objects.create(
user=request.user,
body=comment_body,
post=post
)
created_at = date_format(comment.created_at, format='M. d, Y, h:m a', use_l10n=True)
return JsonResponse({
'user': comment.user.username,
'body': comment.body,
'created_at': created_at
})
In this first template part we just display data, note that {# % if not has_commented % #} is commented out, when first writing the code I limited the number comments to one per User.
post_detail.html (HTML & DTL):
{% extends 'base.html' %}
{% block content %}
<h1>{{post.title}}</h1>
<h4>{{post.created_at}}</h4>
<p>{{post.body}}</p>
<hr>
<div id="comment-div">
{# % if not has_commented % #}
<form action="{% url 'love:comment-create' %}" id="comment-form">
<input type="hidden" id="post-id" value="{{post.id}}">
<textarea id="comment-body" maxlength="255" rows="4" cols="50"></textarea>
<br>
<button type="submit" id="submit-comment">Comment</button>
</form>
<hr>
{# % endif % #}
</div>
<div id="comment-list">
<h2> Comments </h2>
{% if post.comments.all %}
{% for comment in post.comments.all %}
<p>At {{comment.created_at|date:"M. d, Y, h:m a"}} {{comment.user}} commented:</p>
<p>{{comment.body}}</p>
{% endfor %}
{% endif %}
</div>
This second part, containing the <script> is responsible for every event in the template after adding a new comment. The first function is used to retrieve csrftoken value from cookies.
Then, on submit click we collect data and send the AJAX request. Attention to a few lines. First, where csrf header name is set headers: {'X-CsrfToken': csrftoken}. Second, where data is converted to JSON string data: JSON.stringify({ post_id: post_id, body: body }).
post_detail.html (JS):
<script>
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
$('#submit-comment').click( function (e) {
e.preventDefault();
var url = $('#comment-form').attr("action");
var post_id = $('#post-id').attr("value");
var body = $('#comment-body').val();
const csrftoken = getCookie('csrftoken');
$.ajax({
method: "POST",
url: url,
headers: {'X-CsrfToken': csrftoken},
data: JSON.stringify({ post_id: post_id, body: body }),
success: function(comment) {
// $('#comment-div').prop("hidden", true);
$('#comment-body').val('');
$('#comment-list').append(
`<p>At ${comment.created_at} ${comment.user} commented:</p>
<p>${comment.body}</p>`
);
}
});
});
</script>
{% endblock %}
Lastly, on success, clear body input and update the template by appending data to <div id="comment-list">. To limit to one comment per user, uncomment $('#comment-div').prop("hidden", true);

Django json response stay in the same page

I'm making a like button for a post in django. What I need is that when the like button is clicked, the function is executed, but I need the page not to be reloaded (To later use javascript). To do that I return a jsonresponse() instead of a return render. But the real problem is that it redirects me to the page that I show in the photo. The page is not reloaded. as I want it. but I don't want it to show me the blank page with the jsonresponse data (like this photo).I want to stay in the same page without reload.
My view function:
def liking (request, pk):
posts = get_object_or_404(Post, id = pk)
if request.user in posts.likes.all():
posts.likes.remove(request.user)
else:
posts.likes.add(request.user.id)
likes_count = posts.likes.all().count()
print(f'likes_count = {likes_count}')
data= {
'likes_count': likes_count,
}
#return redirect ('index')# This is commented
return JsonResponse(data, safe=False, status=200 )
You can use AJAX. Simply place the code below in the template and trigger it with buttons.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<script>
function get_likes(pk){
$.ajax({
url: '{% url "url-name" pk %}',
type: 'GET',
success: function (res) {
var likes = JSON.parse(res);
return likes["likes_count"]
}
});
}
</script>
If you need to post any data, you can use the lines below.
function get_likes(pk){
$.ajax({
url: '{% url "url-name" pk %}',
type: 'POST',
data: {
csrfmiddlewaretoken: "{{ csrf_token }}",
data1:"data",
data2:"data"
},
success: function (res) {
var likes = JSON.parse(res);
return likes["likes_count"]
}
});
}
You can add the following lines in your function to use the posted data on the django side.
data1 = request.POST.get("data1")
data2 = request.POST.get("data2")
After trying for a while, I found the problem. It had nothing to do with the ajax request or a fetch, which is what I ended up using. It was simply that I had the url of the views.py function in the href="" and for this reason the white screen came out with the jsonresponse() data:
I had to change:
<a class="btn btn-dark like" id="like_button" href="{% url 'liking' post.id %}"> Like</a>
So:
<a class="btn btn-dark like" id="like_button" href="#"> Like</a>
Thanks for all the answers!

I want to add product in cart without refreshing page with AJAX and Django

Nothing will ne happened when I submit product in the the cart. I want to use AJAX without refreshing page.
When I submit the console message will be displayed.
I'm trying to use AJAX first.
Trying to add product in the cart without refreshing page.
I need help please :)
Views Django
def add_cart(request, product_id):
cart = Cart(request)
product = get_object_or_404(Product, id=product_id)
form = CartProductForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
cart.add(product=product,
quantity=cd['quantity'],
update_quantity=cd['update_qt']
)
return JsonResponse({'status': 'success'})
Form
from django import forms
from django.core.validators import MinValueValidator, MaxValueValidator
class CartProductForm(forms.Form):
quantity = forms.IntegerField(initial=1)
update_qt = forms.BooleanField(required=False, initial=False, widget=forms.HiddenInput)
HTML Code
<form action="{% url "..." %}" method="post" data-id="{{ ... }}" class="form-order" id="form">
{{ cart_product_form }}
{% csrf_token %}
<a data-id="{{ ... }}" class="buy-product"><button>BUY</button></a>
</form>
JS Code
$(".buy-product").on('click', function(e){
e.preventDefault();
var product_id = $(this).attr('data-id')
var quantity = 1
console.log(product_id)
console.log(quantity)
data = {
'product_id': product_id,
'quantity': quantity
}
var point='/cart/add/'+product_id+'/'
$.ajax({
headers:{
'Content-Type':'application/json',
'X-CSRFToken':csrftoken,
},
url: point,
type: 'POST',
data: data,
success: function(data){
console.log('success')
console.log(csrftoken)
}
})
})
You need to add csrftoken in data: {csrfmiddlewaretoken':csrftoken}. Also I don't see when you initialize your variables, but I think is just example. Check this answer for more: link
You can prevent form from refreshig the page and then do whatever you need with your JS code:
<form action="{% url "..." %}" method="post" data-id="{{ ... }}" class="form-order" id="form" onsubmit="myFunction(event)">
...
</form>
<script>
function myFunction(event){
event.preventDefault();
}
</script>

My AJAX is working and I want to know why

In my app I made a favorite list that a user can populate using AJAX.
Is it working perfectly but I am not sure if I did it the right way and I would like to know how to do it the proper way. Or maybe somehow I did it right.
My html:
<div class='sub_button'>
<form class="add_btn" method='post'>{% csrf_token %}
<button class='added btn' value= '{{product.id }} {{ sub_product.id }}' ><i class=' fas fa-save'></i></button>
</div>
My AJAX:
$(".row").on('click', ".added", function(event) {
let addedBtn = $(this);
console.log(addedBtn)
event.preventDefault();
var product = $(this).val();
var url = '/finder/add/';
$.ajax({
url: url,
type: "POST",
data:{
'product': product,
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val()
},
datatype:'json',
success: function(data) {
if (data['success'])
addedBtn.hide();
}
});
});
My views.py
def add(request):
data = {'success': False}
if request.method=='POST':
product = request.POST.get('product')
user = request.user
splitted = product.split(' ')
sub_product = Product.objects.get(pk=(splitted[1]))
original_product = Product.objects.get(pk=(splitted[0]))
p = SavedProduct(username= user, sub_product=sub_product, original_product = original_product)
p.save()
data['success'] = True
return JsonResponse(data)
What is bugging me is the way I passed '{{product.id }} {{ sub_product.id }}'. Because I've made it after some tinkering.
There is a better way to do this.
ie, if you have only one product.id and sub_product.id in this page instance. Then in a script tag at the end of the HTML template for this page you add the following:
<script>
var product_id = {{ product.id }}
var sub_product_id = {{ sub_product.id }}
</script>
Then you can link the external JS file in which you have AJAX to this HTML template with another script tag.
Now your product_id and sub_product_id will be available in your external JS script file in which you have the AJAX code.
So that way, you don't have to pass it through the value attribute of the button tag and things look more clean.

Form data not being sent to the server

I have a form that pops up in a modal when the user clicks a button in the search results of a query. This form has three input fields and a few other fields that are being appended to it once the submit button is being clicked using ajax. As this is apart of a Django app, I am catching the values in the view on the server-side. I'm successfully able to get the data from the input fields of the form, but I'm not able to get the data from the appended fields.
I'm fairly new to using ajax so its likely that I'm doing something wrong but it seems like the data should be getting sent to the server. What could be the problem?
Here is the code for the ajax call and the modal form:
<script type="text/javascript">
$("#mform").submit(function(){
var c = getCookie('csrftoken');
//var data1 = $().attr("");
var extraData = [];
extraData['venue'] = $("#invite").data("venue");
extraData['artist'] = $("#invite").data("artist");
extraData['f_date'] = $("#invite").data("formatted_date");
extraData['uf_date'] = $("#invite").data("date");
extraData['ticket_url'] = $("#invite").data("ticket_url");
extraData['city'] = $("#invite").data("city");
extraData['region'] = $("#invite").data("region");
extraData['artist'] = $("#invite").data("artist");
var PostData = {csrfmiddlewaretoken: c, additionalValues:extraData}
$ajax({
//context:this,
type : 'POST',
dataType: 'json',
url: '/artistsearch/',
data: JSON.stringify(PostData),
success: function(response){}
});
});
EDIT:
HTML containing data to be sent
<ul class= 'list-group'>
{% for ticket in data %}
<li class = 'list-group-item' >
Title: {{ticket.title}}
Location: {{ticket.formatted_location}}
Venue: {{ticket.venue.name}}
Date: {{ticket.formatted_datetime}}
tickets status: {{ticket.ticket_status}}<br>
<a href = {{ticket.ticket_url}}> ticket url</a>
{% if user.is_authenticated %}
<button id = 'invite' type='button' class= 'btn btn-info btn-lg' data-toggle = 'modal' data-target='#myModal' data-artist = {{ticket.artists.name}} data-venue={{ticket.venue.name}} data-date = {{ticket.datetime}} data-formatted_date = {{ticket.formatted_datetime}} data-city= {{ticket.venue.city}} data-region = {{ticket.venue.region}} data-ticket_url={{ticket.ticket_url}} > Invite a friend </button>
<button id = 'save' type = 'button' class = 'btn btn-primary-outline'> Save Concert </button>
{% endif %}
</li>
{% endfor %}
</ul>
You have missed the # selector for id invite of button it should be like $("#invite"). Use attribute like data-artist instead of just artist and get it like $("#invite").data("artist) or $("#invite").attr("data-artist).
And then post it like ....
var postData = {
csrfmiddlewaretoken: c,
additionalValues: extraData
}
Then post data like
$.ajax({
type: 'post',
url: 'URL here',
data:JSON.stringify(PostData),
// .......
// Other code here
)};

Categories