Add Like button with Django + Ajax - javascript

Hi I create my first project like stackoverflow(question-answer). I used this guid from Tango with Django http://www.tangowithdjango.com/book17/chapters/ajax.html to add like button with ajax. And nothing hapened. Don't see any request in console. I'm noob in Django, and it's my first encounter with jquery.
apps/questions/models:
class Answer(models.Model):
text = models.TextField()
date = models.DateTimeField(default=datetime.datetime.now)
likes = models.IntegerField(default=0)
resolve = models.IntegerField(default=0)
author = models.ForeignKey(CustomUser)
question = models.ForeignKey(Question)
apps/questions/views:
#login_required
def add_like(request):
ans_id = None
if request.method == 'GET':
ans_id = request.GET['answer_pk']
likes = 0
if ans_id:
ans = Answer.objects.get(id=(int(ans_id)))
if ans:
likes = ans.likes + 1
ans.likes = likes
ans.save()
return HttpResponse(likes)
apps/questions/ulrs:
url:
url(r'add_like/$', views.add_like, name='add_like'),
question.html:
{% for answer in answers %}
<div class="container-fluid no-padding">
{{ answer.text }}
</div>
<div class="container-fluid author-question">
<p>posted: {{ answer.date.day|stringformat:"02d" }}.{{ answer.date.month|stringformat:"02d"}}.{{ answer.date.year}}</p>
<p>by: {{ answer.author.username }}</p>
</div>
{% if user.is_authenticated %}
<button class="btn btn-default" type="button" id="likes" data-ansid="{{ answer.id }}">
like | <strong id="like_count">{{ answer.likes }}</strong>
</button>
{% endif %}
js/ajax.js:
$('#likes').click(function(){
var ansid;
ansid = $(this).attr("data-ansid");
$.get('/questions/add_like/', {answer_id: ansid}, function(data){
$('#like_count').html(data);
$('#likes').hide();
});
});

Since you are creating buttons in a for loop, and naming them the same way, you have multiple elements on the page with the same id. Because of this you get unpredictable results. You should either give each button its own id, or change the jQuery selector to select the buttons based on the appropriate class.
For example, you could have:
{% for answer in answers %}
<div class="container-fluid no-padding">
{{ answer.text }}
</div>
<div class="container-fluid author-question">
<p>posted: {{ answer.date.day|stringformat:"02d" }}.{{ answer.date.month|stringformat:"02d"}}.{{ answer.date.year}}</p>
<p>by: {{ answer.author.username }}</p>
</div>
{% if user.is_authenticated %}
<button class="btn btn-default likes-button" type="button" data-ansid="{{ answer.id }}">
like | <strong id="like_count">{{ answer.likes }}</strong>
</button>
{% endif %}
{% endfor %}
And then for the javascript
$('.likes-button').click(function(){
var ansid;
ansid = $(this).attr("data-ansid");
$.get('/questions/add_like/', {answer_id: ansid}, function(data){
$('#like_count').html(data);
$('#likes').hide();
});
});

Related

Need to save all answers from a user on every questions related on a specific Quiz

I want to save after submitting all answers from a user of all questions related on a specific Quiz.
The logical is :
I've got Quiz, Questions. Question car link to Quiz and when I choose a quiz all questions link to are shown on webpage with javascript (design).
After submiting I need to get all checkbox checked and save into UserResponse.
Models
class Question(models.Model):
objects = None
question = models.CharField(max_length=200, null=True)
description = models.CharField(max_length=255, null=True)
question_pic = models.ImageField(upload_to='poll/', storage=fs, null=True)
choices = models.ManyToManyField(Answer, related_name='QuestionsChoices')
mandatory = models.BooleanField(default=True)
multiple = models.BooleanField(default=False)
randomize = models.BooleanField(default=False)
class Quiz(models.Model):
STATUS_CHOICES = (
(1, 'Draft'),
(2, 'Public'),
(3, 'Close'),
)
nom = models.CharField(max_length=200, null=False)
theme = models.ForeignKey(Theme, null=False, on_delete=models.CASCADE)
questions = models.ManyToManyField(Question, related_name='Quizs')
status = models.IntegerField(choices=STATUS_CHOICES, default=1)
published = models.DateTimeField(null=True)
date_added = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now_add=True)
class QuizInstance(models.Model):
objects = None
"""
A combination of user response and a quiz template.
"""
player = models.ForeignKey(User, on_delete=models.CASCADE)
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
start_quiz = models.DateTimeField(auto_now_add=True)
score = models.IntegerField(default=0)
complete = models.BooleanField(default=False)
class UserResponse(models.Model):
objects = None
"""
User response to a single question.
"""
quiz_instance = models.ForeignKey(QuizInstance, on_delete=models.CASCADE)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
response = models.ManyToManyField(Answer, related_name="UserResponses")
time_taken = models.DateTimeField(auto_now_add=True)
time_taken_delta = models.DateTimeField(blank=True)
VIEWS (to show all questions from selected quiz)
def show_quiz(request, quiz_id):
try:
quiz = Quiz.objects.get(pk=quiz_id)
except:
messages.error(request, "Ce quiz n'existe pas ;)")
return redirect('poll:show-list')
context = {
'quiz': quiz,
}
return render(request, "poll/show_quiz.html", context)
# IF QUIZ SUBMITED, HERE I NEED TO SAVE ALL ANSWER GAVE BY USER INTO UserResponse
def answer_quiz(request):
context = {}
if request.method == 'POST':
quiz = Quiz.objects.get(pk=request.POST.get('id_quiz'))
player = request.user
quiz_player = QuizInstance.objects.filter(player=player, quiz=quiz)
if len(quiz_player) == 0:
quiz_instance = QuizInstance.objects.create(player=player, quiz=quiz)
else:
context = {
'error': True,
}
return render(request, "poll/answer_quiz.html", context)
TEMPLATES (HTML)
[...]
{% for question in quiz.questions.all %}
<fieldset class="p-4" style="max-width:720px; margin:0 auto;">
<question>
{{ forloop.counter }} - {{ question.question }}
{% if question.question_pic != "" and question %}
<div class="col-12 my-3 rounded-3" id="picQuestionDiv">
<img class="img-thumbnail rounded-3 p-2" src="/media/{{ question.question_pic }}" width="200" />
</div>
{% endif %}
</question>
<reponses class="d-grid gap-4" style="display:block !important; ">
{% if question|is_answer_pic %}
{% for reponse in question.choices.all %}
<input type="checkbox" hidden value="{{ reponse.id }}" name="reponse" id="reponse_{{ reponse.id }}" />
<reponse style="width:calc(50% - 8px + 4px); text-align:left !important;" class="col-6 mb-2 btn btn-reponse reponse" data-id="reponse_{{ reponse.id }}">
<div class="d-flex align-items-center rounded-3">
{% if reponse.answer_pic %}
<img class="card-img-top rounded-3 img-fluid mh-100 cover text-center" style="" src="/media/{{ reponse.answer_pic }}">
{% else %}
<i class="card-img-top rounded-3 mt-2 img-fluid mh-100 cover bi bi-eye-slash fs-1 text-center"></i>
{% endif %}
</div>
<div style="display:inline-block">
<span class="border border-secondary p-0 px-2 rounded-3 letter-reponse-pic bg-light text-secondary" style="">{{ forloop.counter0|show_letter }}</span>
<span class="">{{ reponse.answer }}</span>
</div>
</reponse>
{% endfor %}
{% else %}
{% for reponse in question.choices.all %}
<reponse style="width:calc(50% - 8px + 4px);" class="col-6 mb-2">
<div class="d-flex align-items-center rounded-3 border btn btn-outline-reponse p-2 reponse mb-2">
<span class="border border-secondary p-0 px-2 rounded-3 letter-reponse bg-light text-secondary">{{ forloop.counter0|show_letter }}</span>{{ reponse.answer }}
</div>
</reponse>
{% endfor %}
{% endif %}
</reponses>
<link class="" style="display: block;">
{% if not forloop.first %}
<a class="btn btn-secondary prev">Précédente</a>
{% endif %}
{% if not forloop.last %}
<a class="btn btn-secondary next">Suivante</a>
{% endif %}
{% if forloop.last %}
<button type="submit" class="btn btn-success next">Terminé !</button>
{% endif %}
</link>
</fieldset>
{% endfor %}
I know my method is not very clean ... I use form for Create Question, Quiz, etc ... but for showing Question I can't because need to show specific kind of checkbox if image, and other if not ... Anyway, here my need it just to retrive from POST submitting all data from quiz send by user And save it :)
Thanks for your help

Why html buttons dynamically created by django cannot access my javascript function?

My Django application makes divs dynamically. Each div is a post of a blog post and also I have a like button on each div. Each button will automatically turn into unlike when a user clicks on it without reloading using javascript. I wrote a javascript function for this. Unfortunately, my javascript function works only the last post on a page (As I have pagination property).
document.addEventListener("DOMContentLoaded",function(){
// const colon = document.createElement('div');
// colon.setAttribute('id','colon')
// e.preventDefault()
// const likebtn = document.createElement('button');
// likebtn.setAttribute('class','likebtn btn btn-primary');
// likebtn.setAttribute('class','likebtn');
// document.querySelector('.card-footer').appendChild(likebtn);
// document.querySelector('.likebtn').innerHTML = "Like";
document.querySelector(`#likeform${posts_id}`).addEventListener('submit',(event) => {
event.preventDefault();
like_function();
// return false;
})
// document.querySelector('.likepost').addEventListener('click', ()=> like_function('likepost'));
})
// let is_like = "{{is_like}}";
// let num_like = "{{num_like}}";
function like_function(){
// document.createElement('button').innerHTML = "Love";
// console.log("Updated!")
fetch(`/like/${posts_id}`,{
method:"POST",
body : JSON.stringify({
"is_like" : is_like,
"num_like" : num_like,
})
})
.then(response => response.json())
.then(result => {
if(result.is_like){
console.log("function is liked");
console.log(`${result.is_like} for post ${posts_id}`);
let num_like = result.num_like;
console.log(`Number of posts : ${num_like}`);
document.querySelector(`#likebtn${posts_id}`).innerHTML = "Unlike";
num_like = num_like + 1;
console.log(`Number of posts : ${num_like}`);
document.querySelector(`#num_of_likes_${posts_id}`).innerHTML = `${num_like} `
// location.replace("http://127.0.0.1:8000")
}
else{
console.log("function is unliked, back off!");
console.log(`${result.is_like} for post ${posts_id}`);
let num_like = result.num_like;
console.log(`Number of posts : ${num_like}`);
document.querySelector(`#likebtn${posts_id}`).innerHTML = "Like";
num_like = num_like - 1;
console.log(`Number of posts : ${num_like}`);
document.querySelector(`#num_of_likes_${posts_id}`).innerHTML = `${num_like} `
// location.replace("http://127.0.0.1:8000")
}
})
}
// function like_function(){
// if (document.querySelector("#like").style.color == "blue"){
// document.querySelector("#like").style.color = "red";
// }else{
// document.querySelector("#like").style.color = "blue";
// }
// }
This is my full javascript code. Here I'm sharing my HTML template,
<div id="posts" class="card">
<ul class="card-body">
{% for posts in page_view %}
<li class="card">
<div class="card-header bg-success">
<h5 class="card-title"><a class="text-light" style="text-decoration: none;" href="{% url 'profile' posts.user.id %}">{{ posts.user }}</a></h5>
<h6 class="card-subtitle text-light">{{ posts.timestamp }}</h6>
</div>
<div class="card-body">
<h3 class="card-text">{{ posts.post }}</h3>
</div>
<div id="likeapost{{posts.id}}" class="card-footer">
{% if request.user in posts.likepost.all %}
<form action="{% url 'likepost' posts_id=posts.id %}" id="likeform{{posts.id}}" method="POST" style="display: inline;">
<!-- {% csrf_token %} -->
<button id="likebtn{{posts.id}}" class="btn btn-link" type="submit">Unlike</button>
</form>
{% else %}
<form action="{% url 'likepost' posts_id=posts.id %}" id="likeform{{posts.id}}" method="POST" style="display: inline;">
<!-- {% csrf_token %} -->
<button id="likebtn{{posts.id}}" class="btn btn-link" type="submit">Like</button>
</form>
{% endif %}
<small id="num_of_likes_{{posts.id}}" class="num_of_likes">{{ posts.likepost.all.count }}</small>
{% block script %}
<script>
posts_id = "{{ posts.id }}";
is_like = "{{is_like}}";
num_like = "{{ posts.likepost.all.count }}";
</script>
<script src="{% static 'network/controller.js' %}"></script>
{% endblock %}
<button class="btn btn-link" style="text-decoration: none;">Comment</button>
View Post
{% if request.user.id is posts.user.id %}
Edit
{% endif %}
<!-- <div class="likepost"></div> -->
</div>
</li>
{% empty %}
<h6>No post availabel 😔</h6>
{% endfor %}
</ul>
</div>
This is my HTML template. All the buttons should work like this,
Only this one button works perfectly, but others do not. This is what happening.
What should I do? I am about 90% done. Please help.

can't refresh the data in a DIV python Django nor the page after a jquery

Let me start by saying I have 2 variables in an HTML template(messages and users) and I have multiple buttons that when one of them is clicked it calls a jquery code that sends a post request to a Django server and it returns an update to a variable(messages)
however, it's not updating the loop, I also tried to return a new HTML page that contains the new variable updated but the jquery is not updating the whole page with the new HTML
if I can update the variable alone it would be better and if I can't do that how can I make jquery use the new HTML page
the python code i used to return the update to the varialbe messages:
if request.method == 'POST':
send=Message.objects.filter(from_id=request.POST.get('userId'),to_id=2)
rec=Message.objects.filter(from_id=2,to_id=request.POST.get('userId'))
messages=sorted(chain(rec, send),key=lambda instance: instance.id,reverse=True)
print(messages)
return HttpResponse(list(messages))
and the code i used to return new HTML template:
m = Message.objects.filter(to_id=2).order_by('-id')
users = {}
for i in m:
if users.get(i.from_id.username) == None:
users[i.from_id.username] = User.objects.get(id=i.from_id.id)
users = list(users.values())
send=Message.objects.filter(from_id=users[0].id,to_id=2)
rec=Message.objects.filter(from_id=2,to_id=users[0].id)
messages=sorted(chain(rec, send),key=lambda instance: instance.id,reverse=True)
if request.method == 'POST':
send=Message.objects.filter(from_id=request.POST.get('userId'),to_id=2)
rec=Message.objects.filter(from_id=2,to_id=request.POST.get('userId'))
messages=sorted(chain(rec, send),key=lambda instance: instance.id,reverse=True)
print(messages)
return render(request,'psych.html',{"users":users, "messages":list(messages)})
return render(request,'psych.html',{"users":users, "messages":list(messages)})
the HTML code and jquery code that uses the variable and try to update it
function newUser(id){
$.ajax({
type: 'POST',
url:'/psych.html/',
data:{
userId:id,
},
success: function(data){
console.log(data);// the data returnd are correct and as needed
//but i cant make it update the messages
$('#messageDiv').load(document.URL + ' #messageDiv');
}
})
}
{% for i in users %}
<li class="">
<button type="button" class="btn" onClick="newUser({{i.id}})">
<div class="d-flex bd-highlight">
<div class="img_cont">
<!-- here was an image ----------------------------------------------->
</div>
<div class="user_info">
<span>{{i.id}}</span>
</div>
</div>
</button>
</li>
{% endfor %}
<!-- The varialbe that i'm trying to update is called messages bottom -->
{% for o in messages %}
{% if o.to_id.id != 2 %}
<div class="d-flex justify-content-start mb-4">
<div class="img_cont_msg">
<!-- here was an image-->
</div>
<div class="msg_cotainer">
{{o.message}}
<!-- <span class="msg_time">{{o.time}}</span> -->
</div>
</div>
{% else %}
<div class="d-flex justify-content-end mb-4">
<div class="msg_cotainer_send">
{{o.message}}
<!-- <span class="msg_time_send">{{o.time}}</span> -->
</div>
<div class="img_cont_msg">
<!-- here was an image-->
</div>
</div>
{% endif %}
{% endfor %}
if it helps i did it before and updated the messages from jquery but i used form and there was only 1 variable i will add the code to that too
$(document).on('submit','#submitMessage', function (e){
e.preventDefault();
$.ajax({
type: 'POST',
url:'/psych.html/',
data:{
message:$('#messageHolder').val(),
csrfmiddlewaretoken: $('input[message=csrfmiddlewaretoken]').val(),
},
success: function(data){
// it work like charm here
$('#messageDiv').load(document.URL + ' #messageDiv');
}
})
})
{% for o in messages %}
{% if o.to_id.id == 2 %}
<div class="d-flex justify-content-start mb-4">
<div class="img_cont_msg">
<!-- here was an image-->
</div>
<div class="msg_cotainer">
{{o.message}}
<!-- <span class="msg_time">{{o.time}}</span> -->
</div>
</div>
{% else %}
<div class="d-flex justify-content-end mb-4">
<div class="msg_cotainer_send">
{{o.message}}
<!-- <span class="msg_time_send">{{o.time}}</span> -->
</div>
<div class="img_cont_msg">
<!-- here was an image-->
</div>
</div>
{% endif %}
{% endfor %}
<form id="submitMessage" >
{% csrf_token %}
<div class="card-footer">
<div class="input-group">
<div class="input-group-append"></div>
<input name="message" class="form-control type_msg" placeholder="Type your message..." id="messageHolder">
<div class="input-group-append">
<button type="submit" class="btn">
<span class="input-group-text send_btn" ><i class="fas fa-location-arrow"></i></span>
</button>
</div>
</div>
</div>
</form>
Try this
$("#messageDiv").load(location.href+" #messageDiv>*");
i figured the problem and it was because i didn't know that
$("#messageDiv").load(location.href+" #messageDiv>*");
would make a GET request so all I did was adding the necessary data to the URL and then change the URL too(so if the client refreshed the page it would stay in the same spot) without refreshing the page and then do the command app there
if it could help anyone please look at the code below:
function newUser(id){
var url = document.URL;
url = url.split('/');
url[url.length-2] = id;
url = url.join('/');
window.history.pushState("object or string", "my website name", url);
$('#messageDiv').load(url + ' #messageDiv');
}
sadly i don't know how to do post requst then load the page please if you know comment down bellow so someone else might get help from it

Django bootstrap alerts not working as expected

I need help with this.
I'm trying to make my app looks better with bootstrap alert, I have one alert to add an item and other alert to delete an item.
When I add an item my alert looks great and work fine but when I delete the item it's not working properly.. Only shows my message with no bootstrap alert....
What am I doing wrong?
Here's what I got:
<div class="container">
{% if messages %}
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
{% for message in messages %}
<p{% if message.tags == "success" %} class="alert alert-success "{% endif %}>{{ message }}</p>
{% if message == 'danger' %}
<p{% if message.tags == 'danger' %} class="alert alert-danger"{% endif %}>{{ message }}</p>
{% endif %}
{% endfor %}
</div>
</div>
{% endif %}
Views for my success msg
messages.success(request, 'Has been added!.')
Views for my danger msg
messages.error(request, 'Has been deleted!.')
Thanks in advance..!
EDIT
I solved my problem as Silvio answered to my question. worked great with every bootstrap alert but the alert-danger to make it work I had to edit my settings.py to something like this:
from django.contrib.messages import constants as message_constants
MESSAGE_TAGS = {message_constants.DEBUG: 'debug',
message_constants.INFO: 'info',
message_constants.SUCCESS: 'success',
message_constants.WARNING: 'warning',
message_constants.ERROR: 'danger',}
As an update to the author's question, only the tags that are being overridden need to be listed in settings:
https://docs.djangoproject.com/en/4.0/ref/contrib/messages/#message-tags
In this case (Bootstrap looking for "danger", but Django providing "error"):
from django.contrib.messages import constants as messages
MESSAGE_TAGS = {
messages.ERROR: 'danger',
}
I guess you are using the wrong HTML markup for Bootstrap:
{% if messages %}
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
{% for message in messages %}
<div class="alert alert-{{ message.tags }}" role="alert">
<p>{{ message }}</p>
</div>
{% endfor %}
</div>
</div>
{% endif %}
Note that you were using a <p> tag instead of a <div>. Also maybe you can use the {{ message.tags }} directly.

Django: Jquery click function not working in Ajax

I have been working through the Tango with Django exercises to cut my teeth into Django. Almost done but having a problem with the Ajax part.
Ajax function to auto_add a page is not being called. Idk what the problem is since the other functions are being called.
On the shell prompt, there is no call to the ajax function at all. Help needed.
Pertinent code attached. It is the same as on the website link above.
static/rango-ajax.js
$('.rango-add').click(function(){
var catid = $(this).attr("data-catid");
var title = $(this).atrr("data-title");
var url = $(this).attr("data-url");
$.get('/rango/auto_add_page/', {category_id: catid, url: url, title: title}, function(data){
$('#pages').html(data);
me.hide();
});
});
templates/rango/category.html
{% if user.is_authenticated %}
<button data-catid="{{category.id}}" data-title="{{ result.title }}" data-url="{{ result.link }}" class="rango-add btn btn-mini btn-info" type="button">Add</button>
{% endif %}
rango/views.py
#login_required
def auto_add_page(request):
context = RequestContext(request)
cat_id = None
url = None
title = None
context_dict = {}
if request.method == 'GET':
cat_id = request.GET['category_id']
url = request.GET['url']
title = request.GET['title']
if cat_id:
category = Category.objects.get(id=int(cat_id))
p = Page.objects.get_or_create(category=category, title=title, url=url)
pages = Page.objects.filter(category=category).order_by('-views')
#Adds our results list to the template context under name pages.
context_dict['pages'] = pages
return render_to_response('rango/page_list.html', context_dict, context)
rango/urls.py
urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
url(r'^goto/$', views.track_url, name='track_url'),
url(r'^add_category/$', views.add_category, name='add_category'),
url(r'^auto_add_page/$', views.auto_add_page, name='auto_add_page'),
Complete code is at this link.
your code is good, the only thing what you have to do is to define your template in /tango/templates/rango/page_list.html. This template have the following code:
{% if pages %}
<ul>
{% for page in pages %}
<li>
{{ page.title}}
{% if page.views > 1 %}
({{page.views}} views)
{% elif page.views == 1 %}
({{page.views}} view)
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<strong> No Pages currently in category. </strong>
{% endif %}
And inside of your category template you must define the following code:
% if category %}
{% if user.is_authenticated %}
Add a new Page <br>
{% endif %}
{% if pages %}
<div id="pages">
<ul>
{% for page in pages %}
<li>
{{ page.title}}
{% if page.views > 1 %}
({{page.views}} views)
{% elif page.views == 1 %}
({{page.views}} view)
{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% else %}
<strong> No Pages currently in category. </strong>
{% endif %}
{% else %}
The specified category {{ category_name }} does not exist!
{% endif %}
I'm working through this section of the tutorial now and just want to add to Héctor's answer. To avoid duplicating the code to display the list of pages I did the following:
I added a get_page_list() method to tango/rango/templatetags/rango_extras.py, similar to the get_category_list() method used to display a list of categories in an earlier section of the tutorial.
from rango.models import Page
#register.inclusion_tag("rango/page_list.html")
def get_page_list(category):
pages = Page.objects.filter(category=category) if category else []
return {'pages': pages}
Then we just need to load rango_extras and call the get_page_list() method in tango/templates/rango/category.html.
{% extends 'rango/base.html' %}
{% load rango_extras %}
<!-- Existing code -->
{% if category %}
<!-- Existing code to show category likes and like button -->
<div id="page_list">
{% get_page_list category %}
</div>
<!-- Existing code to show search if user is authenticated -->
{% else %]
The specified category {{ category_name }} does not exist!
{% endif %}
This allows you to display the list of pages when a category page is first loaded and then refresh it if a category is added from the search area, without having to duplicate any code.

Categories

Resources