I have a Song model with a votes attribute. I have a Vote as Favourite button displayed below each Song object. I want when a user clicks on the Vote as Favourite button the votes attribute associated with that Song object should increment by 1 and all the Vote as Favourite buttons should be disabled.
HTML
{% for song in dj_song_list %}
<div>
<p><h3>{{ song.name }}</h3></p>
<p class="song_id">{{ song.song_id }}</p>
<button type="button" class="btn btn-default btn-custom" class='vote' onclick="update();">Vote as Favourite</button>
</div>
{% endfor %}
ajax.py
def update_votes(id):
song = Song.objects.get(song_id=id)
song.votes += 1
song.save()
#dajaxice_register
def update_disable(request, song_id):
update_votes(song_id)
dajax = Dajax()
dajax.assign('.vote', 'disabled', 'disabled')
return dajax.json()
JavaScript
function update(){
Dajaxice.hunt.update_disable(Dajax.process,{'song_id':$('.song_id').val()})
}
The button disabling part works fine when used alone. But when I use the update_votes() function in the update_disable() function nothing works. What is wrong with my code?
AJAX part does nothing, because it gets errors. The problem is in the update_votes() function. The problems could be:
Several objects exist with same song_id value
The song with id does not exist
Try using pdb.set_trace() for debugging purposes:
def update_votes(id):
import pdb; pdb.set_trace()
song = Song.objects.get(song_id=id)
I had set the votes attribute of the Song model to default=None. Hence it wasn't able to perform the addition operation. I set it now as default=0 instead.
Related
I have a Django project that has a Students model with multiple fields, and I have implemented a ModelChoiceField form which drops down and allows for selecting a particular record in the Students table.
forms.py:
class StudentChoiceField(forms.Form):
students = forms.ModelChoiceField(
queryset=Student.objects.values_list().order_by("last_name"),
empty_label="(select student)",
widget=forms.Select(attrs={"onChange":'refresh()'})
)
def __init__(self, *args, **kwargs):
super(StudentChoiceField, self).__init__(*args, **kwargs)
# without the next line label_from_instance does NOT work
self.fields['students'].queryset = Student.objects.all().order_by("last_name")
self.fields['students'].label_from_instance = lambda obj: "%s %s" % (obj.last_name, obj.first_name)
The label_from_instance method is overridden, so that the drop-down form displays just two model fields (there are eleven total in the model).
When a student is selected, I want to update some textfields in the page to display the remaining fields of the model. Currently, have implemented a javascript function refresh() which is invoked for the onChange event of the StudentChoiceField form.
index.html (all_students_choice is the StudentChoiceField form):
{% extends "base.html" %}
{% block content %}
<body>
<script>
function refresh(){
var id = document.getElementById("id_students").value;
console.log(id);
}
</script>
<div class="container">
<form method=POST action="">
{% csrf_token %}
{{ all_students_choice }}
</form>
</div>
</body>
{% endblock %}
I have confirmed through the browser console that the javascript function is getting called, and printing the value of the ModelChoiceField form. As expected, after selecting an instance from the dropdown menu the value of the form element is the primary key of the table.
I need advice on the best approach to populate the textfields which I will be adding to display the remaining Student model fields (aside from first and last name). Should these be passed as parameters to the javascript function? Is there a best way to approach this problem.
Answering this question with the approach that that was eventually used, in case it would be of assistance to someone else. Decided to render the same template, but with additional element in the context to reference the selected student. In the initial index home page, the selected_student is None:
def index(request):
....
context = {
'students_choice_ln': students_choice_ln,
'students_choice_fn': students_choice_fn,
'selected_student': None
}
return render(request, 'awards/index.html', context)
For the select function, the selected_student is passed in through the context:
def select(request):
if request.method == "GET":
...
student_id = ...
selected_student = Student.objects.get(pk=student_id)
...
context = {
...
'students_choice_ln': students_choice_ln,
'students_choice_fn': students_choice_fn,
'selected_student': selected_student,
...
}
return render(request, 'awards/index.html', context)
The template can then check whether the selected_student variable is available or not, and then accordingly display the additional fields in a separate div.
If there are any experienced web developers / django developers who see problems with this structure, perhaps they can point them out.
I am developing on localhost: http://127.0.0.1:8000
When I perform some search on the website, I want that if the query does not return results, the New entry button allows the creation of a new article from what has been searched in the search input.
So if I search 'whatever' and there is no article called 'whatever', the button should redirect me to the creation page of the article 'whatever'.
<script>
$( document ).ready(function() {
var newEntryUrl = window.location.hostname+":8000/wiki/_create/?slug="+"{{ search_query }}";
document.getElementById('newEntrybtn').setAttribute('href',newEntryUrl);
});
</script>
{% for article in articles %}
{% block wiki_search_loop2 %}
{% endblock %}
{% empty%}
There is no page created for '{{ search_query }}', would you like to create a page nowee?
<a class="btn btn-success" id="newEntrybtn" role="button"><i class="fa fa-plus" aria-hidden="true"></i> New entry ({{ search_query }})</a>
{% endfor %}
To calculate the url to create the new article, I use this line:
var newEntryUrl = window.location.hostname+":8000/wiki/_create/?slug="+"{{ search_query }}";
If I do an alert(newEntryUrl); it returns the desired result: 127.0.0.1:8000/wiki/_create/?slug=whatever
However, if I click the newEntrybtn button, it redirects me to the following url: http://127.0.0.1:8000/wiki/_search/127.0.0.1:8000/wiki/_create/?slug=whatever
Which is strange to me since at no time have I assigned the href attribute to the button, much less have I assigned it any value. It seems that somehow, by default, it gets the value of the current page.
My question is, how can I remove the current page: http://127.0.0.1:8000/wiki/_search/ so that the button href just has this structure: 127.0.0.1:8000/wiki/_create/?slug=whatever ?
I think you are appending the value twice. Once when the page loads because it is in the document.ready function and once again when the button is clicked. Try writing it in another function and calling the function when the button is clicked.
setAttribute is being called twice
https://codepen.io/sijbc/pen/zYNdrmz
.setAttribute() is being called twice
function createUrl(){
var newEntryUrl = window.location.hostname+":8000/wiki/_create/?slug="+"{{ search_query }}";
var newEntryBtn = document.getElementById('newEntrybtn')
newEntryBtn.addEventListener("click" function(){
newEntryBtn.setAttribute('href',newEntryUrl);
})
The problem is because you are using a relative URL as the href, causing the browser to append this to the current URL you are looking at.
In your example, your button will be set as follows:
<a class="btn btn-success" id="newEntrybtn" role="button" href="127.0.0.1/...">
And clicking on it will append the href to the current URL since the browser will consider it as a resource of the current page.
Following your example, using an absolute URL will allow you to go directly to the URL as you have it set as long as you know the full structure (in your case, you are missing the protocol/scheme):
<a class="btn btn-success" id="newEntrybtn" role="button" href="http://127.0.0.1/...">
Or ideally you should use a relative URL by defining correctly the segment of the URL it represents (defining the path from your host where the resource is located):
<a class="btn btn-success" id="newEntrybtn" role="button" href="/wiki/_create/...">
(notice how the URL starts with a slash and omits the server host and protocol).
It is preferred in most cases to use relative URLs to make your code run regardless of the server (environment), meaning it will always use the same server or protocol of the current URL. But it's up to you based on your needs.
References:
https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL#absolute_urls_vs_relative_urls
I found a good solution without using JavaScript, in just one line of code:
<a class="btn btn-success" href="/wiki/_create/?slug={{ search_query }}" role="button"><i class="fa fa-plus" aria-hidden="true"></i> New entry ({{ search_query }})</a>
Thanks to those who have posted an answer, you have inspired me a lot.
The problem is due to the ajax implementation in my django twitter clone app, the like count for every post is showing the same after clicking the like button.but after the page refresh that is okay. I am near to solve the problem but stuck somehow.
view:
def add_like(request):
if request.method == 'GET':
ans_id = request.GET['id']
user = request.user.profile
liked_tweet = get_object_or_404(Tweet, pk=ans_id)
if ans_id:
# creating instance by sending the Like table fields
instance, created = Like.objects.get_or_create(liker=user, liked_tweet=liked_tweet)
ans = Tweet.objects.get(id=(int(ans_id)))
if ans:
likes = ans.likes + 1
ans.likes = likes
ans.save()
# returns the likes field of a tweet post
return HttpResponse(likes)
the HttpResponse is sending the likes and that creates the problem I guess.
the template:
{% for tw in tweets %}
<div class="blog-post">
<p>
{{ tw.content|safe }}<br><hr>
<small class="small">
লিখসে -
<!-- in the "href" we can pass data like "pk", accessing by the structure the current object is based on-->
{{ tw.tweeter.user.username|capfirst }}
</small>
</p>
{% if user.is_authenticated %}
<button class="btn btn-default likes-button" type="button"
data-ansid="{{ tw.pk }}">Like</button>
<i> Total Likes: </i><em class="like_count">{{ tw.likes }}</em>
{% endif %}
</div>
the ajax script:
$(".likes-button").click(function(e) {
if ($(this).html() == "Like") {
$(this).html('Unlike');
//alert("js working");
// error was there for "data" insted of "attr"
var ansid = $(this).attr("data-ansid");
$.ajax({
url: '{% url "add_like" %}',
type: 'get',
data: {id: ansid}
}).done(function (data) {
alert("success");
$('.like_count').html(data);
//$('#likes').hide();
}).fail(function (err) {
alert(err);
});
}
Thanks in advance.
I think the very first comment by Sayse gives your answer. I am just trying to give a bit more explanation.
So What you have done is after a successful ajax request, you replace existing like count with the data you get from ajax in any the element who have a class named .like_count.
Check In your code $('.like_count').html(data); This select all the elemnt having like_count class and change the html.
Instead, what you should've done is after a successful ajax, change the data only in one place. You need to choose appropriate jquery selector.
Something like .closest() can be used. In that case, use (following code is not tested) $(this).closest('.like_count').html(data); to apeend 'like count' in appropriate element.
Also you can assign dynamic ID to each 'like count' element based on id and then use exect ID Selector.
Hope this helps.
Cheers!
Did you say:
but after the page refresh that is okay
Since your code snippet works, you're simply looking for the likes count incrementation to happen and see the live update in the template.
Well, in theory, here:
The function that increments the like should return with a JSON response of the incremented value from the database.
A client function standing by accepts this JSON response, and updates the template value accordingly.
In Practicals:
See this answer: https://stackoverflow.com/a/31832275/1757321
Ok, so I'm trying to add voting to my website(django based) using Ajax. I have multiple entries in one page, But right now my code only let users vote on the first entry. Please help me with the code so that users can vote on all of them.
First is the html code, basically it's just a vote button for users to vote
{% for answer in answers %}<!-- django template -->
<strong id="vote_count">{{ answer.votes }}</strong> people vote this answer
{% if user.is_authenticated %}
<button id="vote" data-answerid="{{answer.id}}" class="btn btn-primary" type="button">
<span class="glyphicon glyphicon-thumbs-up"></span>
Vote
</button>
{% endif %}
{% endfor %}<!-- end django template -->
Second, below is the django view that process the request
#login_required
def vote_answer(request):
answer_id = None
if request.method == 'GET':
answer_id = request.GET['answer_id']
votes = 0
if answer_id:
answer = Answer.objects.get(id=answer_id)
if answer:
votes = answer.votes + 1
answer.votes = votes
answer.save()
return HttpResponse(votes)
below is the url mapping:
url(r'^like_category/$', views.like_category, name='like_category'),
Finally is the javascript
$('#vote').click(function(){
var answerid;
answerid = $(this).attr("data-answerid");
$.get('/vote_answer/', {answer_id: answerid}, function(data){
$('#vote_count').html(data);
$('#vote').hide();
});
});
Again, my problem is that of all the entries I have in one page, with this code I can only vote the first one. How can modify it so I can vote all of them
You need to use class instead of id on <button>, so that multiple buttons can share the same jQuery event handler.
<button class="vote" data-answerid="...">
Then you can do the following in JavaScript:
$(document).on("click", ".vote", function(){
var answerid;
answerid = $(this).attr("data-answerid");
$.get('/vote_answer/', {answer_id: answerid}, function(data){
$('#vote_count').html(data);
$('#vote').hide();
});
});
This will bind the event handler to click any <button class=vote>.
Also you should do AJAX POST instead of GET by HTTP semantics, because voting is a state changing operation. Otherwise the browser or the web proxies may cache the result (though jQuery have its own cache buster).
I'm using Django and I want my users to confirm that they really want something to be deleted. I'm considering pure Django solution (no Javascript confirmations).
According to what I think,I can create a new page containing "Yes" and "No" buttons. If user presses "Yes", my site will go on and delete the object from the database.
Is it the right way to do deletion without using Javascript? How would you implement the feature if you were me?
I would use Django's built in DeleteView, which will display a confirmation page for an HTTP GET request and perform deletion for an HTTP POST request.
The documentation gives this example:
from django.views.generic.edit import DeleteView
from django.core.urlresolvers import reverse_lazy
from myapp.models import Author
class AuthorDelete(DeleteView):
model = Author
success_url = reverse_lazy('author-list')
I'd recommend reading the documentation for the SingleObjectMixin which explains how to customise the way the view finds the object to delete (the default is to look for an URL keyword argument called pk), and for the TemplateResponseMixin which explains how to customise the template that is used (the default is 'myapp/author_check_delete.html').
This is just one of a number of class-based generic views that make basic operations (displaying a page for a single model instance, for a list of model instances, and handling editing, deletion etc.) very quick and easy to implement.
If you wanted to enhance this with JavaScript later you could always write some unobtrusive JS that detects links to the deletion confirmation page (by looking for a class, or a particular URL) and adds a click handler that pops up a confirmation dialog and then sends a POST request to the URL in the link's href attribute. You would also need to modify the view slightly to return a JSON object when request.is_ajax() is True, so that your JS would know if the deletion had succeeded or failed, which would probably involve overriding one of the methods inherited from the DeletionMixin.
That sounds fine. What I have done a couple of times is to create a confirmation template that can be used anywhere in the application:
{% extends 'base.html' %}
{% block content %}
<div class="confirmation-box">
<p>{{ message }}</p>
<div>
Cancel
<form action="{{ action_link }}" method="post">
<input type="hidden" name="next" value="{{ prev_link }}" />
<input type="submit" value="Send" />
</form>
</div>
</div>
{% endblock %}
You pass to it:
A confirmation message for the user (message)
The url to the page you are in (prev_link)
The url that should be called to perform the action (action_link)
If the user cancels the action, then she/he goes back to the original page.
If the user confirms, then the prev_link is passed as a hidden parameter, so the view can redirect the user to the original page after performing the action (although this is completely optional of course).
Which is pretty much what you propossed in your question.