jquery ajax function undo not working as expected - python

I have a list of words, and want users to be able to click a button if they know a word, then this word will change to 'Known', the css class will change and the field word_is_known will change from False to True. But I also want the user to be able to undo this action. This seems to be working fine the first time, i.e. if the user clicks and then unclicks. But if the user clicks the same word again, it creates another entry into the KnownWord model instead of updating the existing one. I've been playing around with this endlessly, but can't figure it out.
Here is my jquery:
$(document).ready(function() {
var known_words = 0;
var clicked_words = [];
var unclicked_words = [];
$(".word_known").click(function() {
var reference = this;
var objectpk = $(this).data('objectpk');
var userpk = $(this).data('userpk');
$(this).toggleClass('increment');
if ($(this).hasClass('increment')) {
known_words++;
clicked_words.push($(this).data('word'));
add_object = 'add';
$.ajax({
async: false,
url: "/videos/songs/vocab/known/"+objectpk+"/"+userpk+"/",
data: {'action': add_object, 'known_words': known_words, 'clicked_words': clicked_words},
success: function(data) {
$(reference).removeClass("btn-warning");
$(reference).addClass("btn-success");
$(reference).text("Known");
},
failure: function(data) {
alert("There is an error!")
},
contentType: "application/x-www-form-urlencoded; charset=utf-8",
})
console.log(known_words, clicked_words);
}
else {
known_words--;
unclicked_words.push($(this).data('word'));
remove_object = 'remove';
$.ajax({
async: false,
url: "/videos/songs/vocab/known-undo/"+objectpk+"/"+userpk+"/",
data: {'action': remove_object, 'known_words': known_words, 'unclicked_words': unclicked_words},
success: function(data) {
$(reference).removeClass("btn-success");
$(reference).addClass("btn-warning");
$(reference).text("Yes");
},
failure: function(data) {
alert("There is an error!")
},
contentType: "application/x-www-form-urlencoded; charset=utf-8",
})
console.log(known_words, unclicked_words);
}
})
});
My views:
def word_known(request, object_pk, pk_user):
if request.method == 'POST':
pass
elif request.method == 'GET' and request.GET['action'] == 'add':
known_words = request.GET.get('known_words', '')
clicked_words = request.GET.getlist('clicked_words[]')
request.session['known_words'] = known_words
request.session['clicked_words'] = clicked_words
user = request.user
song = models.Song.objects.get(pk=object_pk)
for word in set(clicked_words):
models.KnownWord.objects.get_or_create(word_is_known=True,
word=word, user=user, song=song)
print('The number of known words is {} and clicked words are {}'.format(known_words, clicked_words))
return HttpResponse(json.dumps(clicked_words), content_type='application/json')
def word_known_undo(request, object_pk, pk_user):
if request.method == 'POST':
pass
elif request.method == 'GET' and request.GET['action'] == 'remove':
known_words = request.GET.get('known_words', '')
unclicked_words = request.GET.getlist('unclicked_words[]')
request.session['known_words'] = known_words
request.session['unclicked_words'] = unclicked_words
user = request.user
song = models.Song.objects.get(pk=object_pk)
for word in set(unclicked_words):
models.KnownWord.objects.filter(word=word,
user=user, song=song).update(word_is_known=False)
print('The number of known words is {} and deleted words are {}'.format(known_words, unclicked_words))
return HttpResponse(json.dumps(unclicked_words), content_type='application/json')
The KnownWord model:
class KnownWord(models.Model):
word_is_known = models.BooleanField(default=False)
word = models.CharField(max_length=25)
user = models.ForeignKey(User, related_name="known_words", on_delete=models.CASCADE)
song = models.ForeignKey(Song, on_delete=models.CASCADE, null=True, blank=True)
movie = models.ForeignKey(Movie, on_delete=models.CASCADE, null=True, blank=True)
And the relevant part from my template:
....
{% elif item.0 in known_words %}
Known
{% else %}
Yes
{% endif %}

Your problem here is you didn't remove the value from the other array
Its all about arrays push , check , splice take a look at the next example
$(document).ready(function(){
var clicked_words = [] ;
var unclicked_words = [];
var known_words = 0;
$(document).on('click' ,'.word_to_know:not(.pending)' , function(){ //<<<<<<<<<<< here
var This = $(this);
var This_data_word = This.data('word');
This.addClass('pending').toggleClass('increment');
if(This.hasClass('increment')){
known_words++;
clicked_words.push(This_data_word);
unclicked_words = remove_if_in_array(unclicked_words , This_data_word);
// inside ajax callback //<<<<<<< here
setTimeout(function(){ //<< don't use this .. this is just for the demo
This.removeClass('pending'); // <<<<<<<< here
} , 2000); //<< don't use this .. this is just for the demo
}else{
known_words--;
unclicked_words.push(This_data_word);
clicked_words = remove_if_in_array(clicked_words , This_data_word);
// inside ajax callback //<<<<<<< here
setTimeout(function(){ //<< don't use this .. this is just for the demo
This.removeClass('pending'); // <<<<<<<< here
} , 2000); //<< don't use this .. this is just for the demo
}
console.log('known_words '+known_words);
console.log('clicked_words [' +clicked_words +']');
console.log('unclicked_words [' + unclicked_words+']');
});
});
// remove value if is in the array
function remove_if_in_array(array , value){
var index = array.indexOf(value);
if (index > -1) {
array.splice(index, 1);
}
return array;
}
// update value if is in the array
function update_if_in_array(array , value){
var index = array.indexOf(value);
if (index > -1) {
array.splice(index, 1);
}
array.push(value);
return array;
}
a.increment{
border : 1px solid #000;
background : green;
color : #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Known
Yes
I make a single class for all <a> named it word_to_know you can change anything whatever you want .. But I hope you got the idea

Related

500 (Internal Server Error) and Unexpected token < in JSON at position 0

I'm trying to create a website based on guides. When it came to updating the basket through the buttons (decrease and increase), then trying to click on them, errors are issued: Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0, which refers to
.then((data) => {
location.reload()
});
cart.js:24 POST http://127.0.0.1:8000/update_item/ 500 (Internal Server Error)
which refers to
fetch(url, {
method:'POST',
headers:{
'Content-Type':'application/json',
'X-CSRFToken': csrftoken,
},
and i have error in console pycharm line 104, in updateItem
productId = data['productId']
KeyError: 'productId'
code cart.js:
var updateBtns = document.getElementsByClassName('update-cart')
for (i = 0; i < updateBtns.length; i++) {
updateBtns[i].addEventListener('click', function () {
var productId = this.dataset.stuff
var action = this.dataset.action
console.log('productId:', productId, 'Action:', action)
console.log('USER:', user)
if (user == 'AnonymousUser') {
console.log('User is not authenticated')
} else {
updateUserOrder(productId, action)
}
})
}
function updateUserOrder(productId, action){
console.log('User is authenticated, sending data...')
var url = '/update_item/'
fetch(url, {
method:'POST',
headers:{
'Content-Type':'application/json',
'X-CSRFToken': csrftoken,
},
body:JSON.stringify({'productId':productId, 'action':action})
})
.then((response) => {
return response.json();
})
.then((data) => {
location.reload()
});
}
views.py
def updateItem(request):
data = json.loads(request.body)
productId = data['productId']
action = data['action']
print('Action:', action)
print('Product:', productId)
customer = request.user.customer
product = Stuff.objects.get(id=productId)
order, created = Order.objects.get_or_create(customer=customer, complete=False)
orderItem, created = OrderItem.objects.get_or_create(order=order, product=product)
if action == 'add':
orderItem.quantity = (orderItem.quantity + 1)
elif action == 'remove':
orderItem.quantity = (orderItem.quantity - 1)
orderItem.save()
if orderItem.quantity <= 0:
orderItem.delete()
return JsonResponse('Item was added', safe=False)
Thank you in advance
What is your Python version?
From Python v3.0, json.loads() will only accept unicode string.
Maybe the problem is that you have to decode request.body first :
data = json.loads(request.body.decode('utf-8'))
If this is not the problem try to check what you are receiving. Because the "<" character looks like html or xml. For example, change your function to:
def updateItem(request):
data = json.loads(request.body)
return HttpResponse(data)

Error trying to save data in django via ajax(fetch)

I have a model that references other models, I am trying to save data using ajax
Example:
class Friend(models.Model):
name = ...
class Main(models.Model):
name = ....
friend = models.ForeignKey(Friend, on_delete=models.CASCADE)
All body comes from ajax(fetch) request
I have a table (html), and add data to cells, then with the
enter event, send data.
Like this:
input.addEventListener("keyup", function (e) {
//in this scenario I already have the whole row
// get full_row `row_data`
post_ajax = {
method: "POST",
headers: {
"X-CSRFToken": crf_token, // I get it with a regular expression
"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest",
Accept: "application/json",
},
body: JSON.stringify(row_data),
};
fetch("my_url", post_ajax)
.then((res) => res.json())
.catch((error) => console.error("Error:", error))
.then((response) => console.log("Success:", response));
});
My view function
def save_post(request):
if request.is_ajax and request.method == "POST":
body_unicode = request.body.decode('utf-8')
data = json.loads(body_unicode)
print('here the data arrives',data)
# here the data arrives {'name': 'Ale', 'friend_id': 22}
Main.objects.create(name=data['name'], friends=data['friend_id'])
return JsonResponse({"instance": data}, status=200)
return JsonResponse({"error": ""}, status=400)
This is the error
raise TypeError("%s() got an unexpected keyword argument '%s'" %
(cls.__name__, kwarg))
TypeError: Main() got an unexpected keyword argument 'Friends'
Any idea or suggestion?
EDIT:
When you are creating the Main object, try making the "friend" attribute an object, like this:
friend = Friend.objects.get(id=data['friend_id'])
Main.objects.create(name=data['name'], friend=friend)
Also, the main issue appears to be you are calling the column "friends" but it should be "friend" when you are creating the Main object.
This:
Main.objects.create(name=data['name'], friends=data['friend_id'])
Should be:
Main.objects.create(name=data['name'], friend=data['friend_id'])
PREVIOUS ANSWER:
Assuming you are using JQuery in the template to send an AJAX request, since you did not specify.
In your urls.py:
...
path('/api/post_friend/', post_friend_api, name="post_friend_api"),
...
In your template :
<script type="text/javascript">
$("#myBurron").click(function(){
var csrfToken = $( "input[name='csrfmiddlewaretoken']"); // assuming this is a form
var friend_name = $("#friend_name").val();
$.ajax({ url: '{% url 'post_friend_api' %}',
type: "POST",
dataType: "json",
data: {'friend':friend_name, 'csrfmiddlewaretoken':csrfToken.val()},
cache: false
}).done(function(data) {
if (data.result === true){
alert(data.message);
}
});
});
});
</script>
In your views.py:
from django.http import JsonResponse
def post_friend_api(request):
data = {}
if request.POST.get('friend', None) is not None:
friend_name = request.POST.get('post_note')
# save the object and indicate success
data['result'] = True
data['message'] = "Friend saved successfully"
...
if request.is_ajax():
return JsonResponse(data)
else:
return HttpResponseBadRequest()
When you are sending data via POST don't forget to pass along your CSRF token as in the example above. This assumes you have a form on the page you can get it from, otherwise you can use something like this to get it:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var 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;
}
var csrftoken = getCookie('csrftoken');
If you don't want to deal with the CSRF token, you can mark the view with the #csrf_exempt decorator and remove the 'csrfmiddlewaretoken' data element from the Ajax call in the template, but it may not be ideal or the most secure. An example of that:
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
#csrf_exempt()
def post_note_api(request):
...
If you post more details I can update my answer.

Django take value from url

I have problem with take value from url (?site=value). When I had function in views.py it was work, but now I moved this to another file. Can someone solve this problem?
functionAjax.py:
def htmlMain(request):
if request.is_ajax and request.method == "POST":
UrlCut = request.GET.get('site','Main')
Messages = NewsMessage.objects.all().order_by('-Data').values()
context = {
"Messags" : Messages
}
return render(request, 'ajax'+UrlCut+'.html', context)
AjaxFunction.js:
$.urlParam = function(name){
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (results==null){
return null;
}
else{
return results[1] || 0;
}
}
let tech = ""
if($.urlParam('site') != null)
{
tech = "?site=" + $.urlParam('site');
}
UrlSet = "/get/ajax/validate/MainSite"+tech;
$.ajax({
url: UrlSet,
data: $('#FormSite').serialize(),
type: "POST",
async:false,
success: function(response) {
$("#AjaxChange").replaceWith(response);
},
error: function(data)
{
alert('Bad connection');
console.log(data);
}
});
use 'Site' instead of 'site' to get Site=value
UrlCut = request.GET.get('Site', 'Main')

FullCalendar in Django

So, I have an appointment models
class Appointment(models.Model):
user = models.ForeignKey(User)
date = models.DateField()
time = models.TimeField()
doctorName = models.CharField(max_length=50)`
And I want to implement this in the FullCalendar tool. I'm not sure how to even begin. Any help is appreciated. Thanks.
Since your question shows you haven't tried anything , guessing you know javascript and tried some hands on full calendar js.
Suppose you have model named Event for displaying different events in calendar.
class Events(models.Model):
even_id = models.AutoField(primary_key=True)
event_name = models.CharField(max_length=255,null=True,blank=True)
start_date = models.DateTimeField(null=True,blank=True)
end_date = models.DateTimeField(null=True,blank=True)
event_type = models.CharField(max_length=10,null=True,blank=True)
def __str__(self):
return self.event_name
In your views.py
def event(request):
all_events = Events.objects.all()
get_event_types = Events.objects.only('event_type')
# if filters applied then get parameter and filter based on condition else return object
if request.GET:
event_arr = []
if request.GET.get('event_type') == "all":
all_events = Events.objects.all()
else:
all_events = Events.objects.filter(event_type__icontains=request.GET.get('event_type'))
for i in all_events:
event_sub_arr = {}
event_sub_arr['title'] = i.event_name
start_date = datetime.datetime.strptime(str(i.start_date.date()), "%Y-%m-%d").strftime("%Y-%m-%d")
end_date = datetime.datetime.strptime(str(i.end_date.date()), "%Y-%m-%d").strftime("%Y-%m-%d")
event_sub_arr['start'] = start_date
event_sub_arr['end'] = end_date
event_arr.append(event_sub_arr)
return HttpResponse(json.dumps(event_arr))
context = {
"events":all_events,
"get_event_types":get_event_types,
}
return render(request,'admin/poll/event_management.html',context)
And finally in your template setup full calendar with including necessary CSS,JS Files and HTML code.And then ,
<script>
$(document).ready(function() {
$('#calendar').fullCalendar({
defaultDate: '2016-07-19',
editable: true,
eventLimit: true, // allow "more" link when too many events
events: [
{% for i in events %}
{
title: "{{ i.event_name}}",
start: '{{ i.start_date|date:"Y-m-d" }}',
end: '{{ i.end_date|date:"Y-m-d" }}',
},
{% endfor %}
]
});
});
</script>
Dynamically on some event you need to change events for example by changing dropdown you need to filter events ,
$(document).ready(function(){
$('.event_types').on('change',function(){
var event_type = $.trim($(this).val());
$.ajax({
url: "{% url 'manage-event' %}",
type: 'GET',
data:{"event_type":event_type},
cache: false,
success: function (response) {
var event_arr = $.parseJSON(response);
$('#calendar').fullCalendar('removeEvents');
$('#calendar').fullCalendar('addEventSource', event_arr);
$('#calendar').fullCalendar('rerenderEvents' );
},
error: function () {
alert("error");
}
})
})
})
You can use following code to add, remove, update event in full calendar:
model:
class Events(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255,null=True,blank=True)
start = models.DateTimeField(null=True,blank=True)
end = models.DateTimeField(null=True,blank=True)
def __str__(self):
return self.name
view:
def calendar(request):
all_events = Events.objects.all()
context = {
"events":all_events,
}
return render(request,'calendar.html',context)
def all_events(request):
all_events = Events.objects.all()
out = []
for event in all_events:
out.append({
'title': event.name,
'id': event.id,
'start': event.start.strftime("%m/%d/%Y, %H:%M:%S"),
'end': event.end.strftime("%m/%d/%Y, %H:%M:%S"),
})
return JsonResponse(out, safe=False)
def add_event(request):
start = request.GET.get("start", None)
end = request.GET.get("end", None)
title = request.GET.get("title", None)
event = Events(name=str(title), start=start, end=end)
event.save()
data = {}
return JsonResponse(data)
def update(request):
start = request.GET.get("start", None)
end = request.GET.get("end", None)
title = request.GET.get("title", None)
id = request.GET.get("id", None)
event = Events.objects.get(id=id)
event.start = start
event.end = end
event.name = title
event.save()
data = {}
return JsonResponse(data)
def remove(request):
id = request.GET.get("id", None)
event = Events.objects.get(id=id)
event.delete()
data = {}
return JsonResponse(data)
urls:
from .views import calendar, add_event, update, remove, all_events
url('^calendar', calendar, name='calendar'),
url('^add_event$', add_event, name='add_event'),
url('^update$', update, name='update'),
url('^remove', remove, name='remove'),
url('^all_events', all_events, name='all_events')
html:
<html>
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.css"/>
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.min.js"></script>
<script>
$(document).ready(function () {
var calendar = $('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
events: '/all_events',
selectable: true,
selectHelper: true,
editable: true,
eventLimit: true,
select: function (start, end, allDay) {
var title = prompt("Enter Event Title");
if (title) {
var start = $.fullCalendar.formatDate(start, "Y-MM-DD HH:mm:ss");
var end = $.fullCalendar.formatDate(end, "Y-MM-DD HH:mm:ss");
$.ajax({
type: "GET",
url: '/add_event',
data: {'title': title, 'start': start, 'end': end},
dataType: "json",
success: function (data) {
calendar.fullCalendar('refetchEvents');
alert("Added Successfully");
},
error: function (data) {
alert('There is a problem!!!');
}
});
}
},
eventResize: function (event) {
var start = $.fullCalendar.formatDate(event.start, "Y-MM-DD HH:mm:ss");
var end = $.fullCalendar.formatDate(event.end, "Y-MM-DD HH:mm:ss");
var title = event.title;
var id = event.id;
$.ajax({
type: "GET",
url: '/update',
data: {'title': title, 'start': start, 'end': end, 'id': id},
dataType: "json",
success: function (data) {
calendar.fullCalendar('refetchEvents');
alert('Event Update');
},
error: function (data) {
alert('There is a problem!!!');
}
});
},
eventDrop: function (event) {
var start = $.fullCalendar.formatDate(event.start, "Y-MM-DD HH:mm:ss");
var end = $.fullCalendar.formatDate(event.end, "Y-MM-DD HH:mm:ss");
var title = event.title;
var id = event.id;
$.ajax({
type: "GET",
url: '/update',
data: {'title': title, 'start': start, 'end': end, 'id': id},
dataType: "json",
success: function (data) {
calendar.fullCalendar('refetchEvents');
alert('Event Update');
},
error: function (data) {
alert('There is a problem!!!');
}
});
},
eventClick: function (event) {
if (confirm("Are you sure you want to remove it?")) {
var id = event.id;
$.ajax({
type: "GET",
url: '/remove',
data: {'id': id},
dataType: "json",
success: function (data) {
calendar.fullCalendar('refetchEvents');
alert('Event Removed');
},
error: function (data) {
alert('There is a problem!!!');
}
});
}
},
});
});
</script>
</head>
<body>
<br/>
<h2 align="center">title</h2>
<br/>
<div class="container">
<div id="calendar"></div>
</div>
</body>
</html>
Example:To add event to fullcalendar you should add event to your model or if you don't want to save them you should create event and send to fullcalendar(in def calendar). For example to add event to all Sundays of 2020:
view:
def calendar(request):
from datetime import date, timedelta
d = date(2020, 1, 1)
d += timedelta(days=6 - d.weekday()) # First Sunday
all_sunday_in_2020 = []
while d.year != 2021:
all_sunday_in_2020.append({'name': 'my-title', 'start': d, 'end': d
+ timedelta(days=1)})
d += timedelta(days=7)
return render(request,'calendar.html',{'events':all_sunday_in_2020})

POST request to view function

I want to send POST request to view function. Now I am getting 500 error. I could not figure out where is the problem. The view function is receiving POST request but not returning any data.
EDIT: Now problem with 500 error solved. But how should I return form for editing object?
Template:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// 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;
}
var csrftoken = getCookie('csrftoken');
function editParticipant(event_id, row_id){
var postdata = {
'csrfmiddlewaretoken': csrftoken
}
$.post( "/edit-participant-" + event_id + "/", postdata, function( data ) {
console.log(data);
});
}
View:
def edit_participant(request, participant_id):
participant = Participant.objects.get(pk=participant_id)
form = RegisterToEvent(request.POST or None, instance = participant)
if form.is_valid():
form.save()
return HttpResponseRedirect('/event-%s' %2)
data = {'form': form}
return JsonResponse(data)
URL:
url(r'^edit-participant-(?P<participant_id>[0-9]+)/$', 'event.views.edit_participant', name='edit_participant'),
Your view doesn't return a response
def edit_participant(request, participant_id):
return JsonResponse({'data':"Test"})
You should also turn on debug in the settings by setting DEBUG to true, it will tell you exactly what the error is, just remember to turn this off before production. You can also implement error logging.
There isn't anything about this function that needs to use ajax requests. If you want to keep the same page if the form isn't valid, then just return the form errors and do something with them
data = { 'errors': form.errors }
But this doesn't make much sense to me when you can just load the page again with a form and let django do it for you with its form rendering.
return render(request, 'yourhtml.html', {form: form})

Categories