Not able to retrieve variable in template in Django ajax call - django

I am trying retrieve counter value passed from view in the html template .It is coming as None, however in the view print and in ajax success log value it is coming correct.
JS ajax func
$(".pos").click(function(){
counter=counter+1;
$(".add").html('add counter '+counter);
$.ajax(
{ url :'page',
type :'get' ,
data:{
quantity: counter
},
success:function(response){
console.log(response);
}
}
);
My view
def page(request):
counter=request.GET.get('quantity')
print(counter) ## this is printing fine
return render(request,'page.html',{'counter':counter})
html template
<body>
{{counter}}
<button class="btn neg" name="neg-btn">-</button>
<button class="btn add" name="add-btn" >add to box</button>
<button class="btn pos" id="test" name="pos-btn">+</button>
</body>
getting this counter as None

Usually you would have two views, one to render the template and another for your ajax request. This helps to keep your code more organized and have functions do exactly one task as most of the its described in its naming.
One important thing that is happening in your view, is that you are returning the HTML generated by the render API and not the counter value. Of course, there are many ways to do this correctly, but here is one possible solution:
page.html
{% extends 'base.html'%}
{% block content %}
<body>
<div id="counter">
{{counter}}
</div>
<button class="btn neg" id="neg" name="neg-btn">-</button>
<button class="btn add" id="add" name="add-btn" >add to box</button>
<button class="btn pos" id="pos" name="pos-btn">+</button>
</body>
{% endblock content %}
{% block script %}
<script>
let counter = parseInt("{{counter}}");
let page_url = "{% url 'core:page' %}";
console.log(page_url)
$( "#pos" ).click(function() {
counter += 1;
$( "#counter" ).replaceWith(`<div id="counter">${counter}</div>`);
});
$( "#neg" ).click(function() {
counter -= 1;
$( "#counter" ).replaceWith(`<div id="counter">${counter}</div>`);
});
$( "#add" ).click(function() {
$.ajax({
url :page_url,
type :'post' ,
data:
{
counter: counter,
csrfmiddlewaretoken: '{{ csrf_token }}',
},
success:function(response){
console.log(response);
}
});
});
</script>
{% endblock script %}
views.py
from django.http import JsonResponse
def page(request):
counter = 0
if request.method == 'POST':
counter = int(request.POST.get('counter'))
print(counter)
return JsonResponse({'counter': counter})
return render(request,'page.html',{'counter':counter})

Related

How to get specific id from for loop in django template?

I'm working with django and javascript, I'm trying to follow and unfollow multiple users without refreshing the page. therefore i'm using ajax. The problem which i'm facing right now is that the first follower is getting follow and unfollow no matter if i click on that user or on other user.. its an obvious behavior because i couldn't understand how to get specific id of user from the for loop so that i can use that user in a js function.
{% if follower.user in request.user.userprofile.follower.all %}
<span><a class="btn btn-success" id="follow-button{{follow.id}}" toggle="{{follower}}" type="submit">{% csrf_token %}UnFollow</a></span>
{% else %}
<span><a class="btn btn-warning" id="follow-button{{follow.id}}" toggle="{{follower}}" type="submit">{% csrf_token %}Follow</a></span>
{% endif %}
</div><!--/ followers-body -->
{% endfor %}
<script>
$(document).on('click','a[id^=follow-button]', function (e) {
var user = $('#follow-button').attr('toggle'); //this user is coming the first use of looping object..no matter if i click on the 2nd or 3rd user of the loop.
console.log(user,'im im im tested');
e.preventDefault();
$.ajax({
type: 'POST',
url: '{% url "profiles:toggle" %}',
data: {
user_toggle: user,
csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
action: 'POST',
},
success: function (json) {
result = json['result']
console.log(result,"maii mai maima maima a")
if (result) {
document.getElementById("is_following").innerHTML = '<a class="btn btn-success" id="follow-button" toggle="{{user.userprofile}}" type="submit">{% csrf_token %}UnFollow</a>'
}
else
document.getElementById("is_following").innerHTML = '<a class="btn btn-warning" id="follow-button" toggle="{{user.userprofile}}" type="submit">{% csrf_token %}Follow</a>'
},
error: function (xhr, errmsg, err) {
}
});
})
</script>
It is more of JS/JQuery question than a Django one, but here is what I think you are missing.
When you add a listener to an element, you have an option to get the element that was interacted with using the saved word this.
Your problem in the code is that line where you retrieve user. If you replace $('#follow-button') with $(this) then you will get the user that was clicked.
From there you can retrieve the id easily.

My "cart" cookie in the request passed to my Django view is empty despite it showing in browser storage

I have a store page on my site (written in Django w/ a Passenger server) where users can add products to their shopping cart. The cart is stored as a cookie. When they proceed from the store page to the cart page, the cart view should list all of the items in their cart (which it gets from the cart cookie in the request). This works fine when I run it locally. However, when I run this in production, it almost always says the cart is empty. It only lists the cart items properly if I do a hard refresh of the page.
I've added some print statements to the server, and I can see that the view for the page is being called twice in prod (it's only called once in dev). The first time the view is called, the cart cookie has the correct values. The second time it's called however, the cart cookie in the request is an empty object {}. All other cookies in the request look normal (session id, csrftoken, etc). What's very strange is I can see in the browser's developer panel that the cart cookie is populated in both the request's header cookie tab as well as the storage tab.
Django view/utility functions:
def cart_view(request):
data = cart_data(request)
context = {
'items': data['items'],
'order': data['order'],
'cart_items': data['cart_items'],
}
return render(request, 'store/cart.html', context)
def cart_cookie(request):
try:
cart = json.loads(request.COOKIES['cart'])
except:
cart = {}
return cart
def cart_data(request):
cart = cart_cookie(request)
items = []
order = {'get_cart_total': 0, 'get_cart_items': 0, 'shipping': False}
cart_items = order['get_cart_items']
'''
Logic to parse the cart cookie
'''
return {
'items': items,
'order': order,
'cart_items': cart_items,
}
Here's the functions on the store page to populate the cart:
var updateBtns = document.getElementsByClassName('update-cart');
for (var i = 0; i < updateBtns.length; i++) {
updateBtns[i].addEventListener('click', function() {
var productId = this.dataset.product;
var action = this.dataset.action;
updateCartCookie(productId, action);
})
}
function updateCartCookie(productId, action) {
if (action == 'add') {
if (cart[productId] === undefined) {
cart[productId] = {'quantity':0};
}
cart[productId]['quantity'] += 1;
} else if (action == 'remove') {
cart[productId]['quantity'] -= 1;
if (cart[productId]['quantity'] <= 0) {
delete cart[productId];
}
}
document.cookie = 'cart=' + JSON.stringify(cart) + ";domain=;path=/;SameSite=Strict;Secure;";
location.reload();
}
This code is in the page header to initialize the cookies. It's not being called on navigation to the cart page.
<script type="text/javascript">
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'); // this is just a string
var cart = JSON.parse(getCookie('cart')); // this is a json object, so we need to parse it
if (cart == undefined) {
cart = {};
document.cookie = 'cart=' + JSON.stringify(cart) + ";domain=;path=/;SameSite=Strict;Secure;";
}
</script>
And finally, here are the templates for the Store and Cart pages
#Store
{% block content %}
{% load static %}
<div class="container">
<h1 class="page-title">Store</h1>
<div class="row" style="margin-bottom: 20px">
{% for product in products %}
<div class="col-lg-4" style="margin-bottom: 20px">
<img class="thumbnail" src="{{product.imageURL}}">
<br>
<div class="box-element product">
<div>
<h4><strong>{{product.name}}</strong></h4>
<hr>
<p>
{{product.description}}
</p>
</div>
<div style="display: flex; justify-content: space-between; align-items: center;">
<h4>{{ product.price|floatformat:-2 }}</h4>
<button data-product={{product.id}} data-action="add" class="btn light update-cart">Add</button>
</div>
</div>
</div>
{% endfor %}
</div>
<div>
<a style="float: right; margin: 5px;" class="btn dark" href="{% url 'cart' %}">Cart</a>
</div>
</div>
{% endblock content %}
#Cart
{% block content %}
<div class="container">
<h1 class="page-title">Cart</h1>
<div class="">
<a class="btn button light" href="{% url 'store' %}">← Store</a>
<br>
<br>
{% if cart_items == 0 %}
<p style="text-align: center;">Your cart is empty.</p>
{% else %}
<div>
<div class="cart-row">
<div class="shrinking-flex-column-2-1"><strong>Item</strong></div>
<div class="static-flex-column-1"><strong>Price</strong></div>
<div class="static-flex-column-1"><strong>Quantity</strong></div>
<div class="static-flex-column-1"><strong>Total</strong></div>
</div>
{% for item in items %}
<div class="cart-row" style="align-items: center;">
<div class="shrinking-flex-column-2-1">{{item.product.name}}</div>
<div class="static-flex-column-1">${{item.product.price}}</div>
<div class="static-flex-column-1">
<p class="quantity">x{{item.quantity}}</p>
<div class="quantity">
<img class="chg-quantity update-cart" src="{% static 'store/images/arrow-up.png' %}" data-product={{item.product.id}} data-action="add" >
<img class="chg-quantity update-cart" src="{% static 'store/images/arrow-down.png' %}" data-product={{item.product.id}} data-action="remove" >
</div>
</div>
<div class="static-flex-column-1">${{item.get_total}}</div>
</div>
{% endfor %}
<div style="display: flex; justify-content: space-between; align-items: center;">
<div>
<h5>Total Order Items: <strong>{{order.get_cart_items}}</strong></h5>
<h5>Total Order Amount: <strong>${{order.get_cart_total}}</strong></h5>
</div>
<a class="btn dark" role="button" href="{% url 'checkout' %}">Checkout</a>
</div>
</div>
{% endif %}
</div>
</div>
{% endblock content %}
This ended up being entirely a caching issue. Adding the #never_cache decorated to the cart/checkout views fixed the problem.
How to set cache control headers in a Django view class (no-cache)

Django and Ajax refreshing a div

I am very new to JS/Ajax and a relative novice to Django. I am testing to see how I can get information back and forth from an Ajax call to my view, and back to Ajax to replace a header string as a simple test.
I can receive information back to my view (h1, which is "AJAX TEST"). What I can't seem to get is the h2text to change to "HEADER CHANGED".
Any help is appreciated as I don't know if it's my Django return function or my Ajax function.
Here is my ajax_test.html:
<div class="content">
<div class="center">
<h1 id="h1">AJAX TEST</h1>
<div>
<button id="btn">PRESS ME</button>
</div>
</div>
<div id="update_div">
{% include "ProdPlat/ajax_test_div.html" %}
</div>
</div>
<script type="text/javascript" src="{% static 'ProdPlat\js\ajax_test.js' %}"></script>
{% endblock %}
My ajax_test_div.html (which I'm trying to refresh):
<div class="content">
<div class="center">
<h2 id="h2">{{h2text.h2text}}</h2>
</div>
</div>
My views_test.py:
def test_ajax(request):
h2text = {'h2text': "INITIAL HEADER"}
if request.method == 'POST':
logger.debug('POST request')
text = request.POST.get('text')
logger.debug('text = {}'.format(text))
h2text = "HEADER CHANGED"
dict = {
'h2text': h2text,
}
# return JsonResponse(dict)
# return render(request, 'ProdPlat/ajax_test_div.html', {'h2text': h2text})
return render_to_response('ProdPlat/ajax_test_div.html', dict)
return render(request, 'ProdPlat/ajax_test.html', {'h2text': h2text})
And finally my Ajax call:
$(document).on('click', 'button', function(){
var csrftoken = getCookie('csrftoken');
console.log('Button Clicked!');
var text = $('h1').text()
console.log('h1 text = ' + text);
$.ajax({
type : 'POST',
url : "/ProdPlat/Ajax_Test/",
dataType: 'json',
data: {'text' : text,
'csrfmiddlewaretoken': csrftoken,
},
success: function(data) {
console.log('data = ' + data);
console.log('data.h2text = ' + data.h2text);
$('#update_div').load(' #update_div')
console.log('Success!');
},
failure: function(data) {
console.log('Failed!');
console.log('data = ' + data);
},
})
})

Django - POST method not working for those forms created within a FOR loop in template

I'm using a for loop in a template to create multiple forms with method="post" that work with Ajax. But only the form for the first element of items_list works fine, the rest do not work at all showing error 405 0 Method Not Allowed. I think they all should work the same way. And just wondering if this issue was caused by a for loop or something else.
cart_items.html:
<script>
$(document).ready(function () {
$("#remove").click(function (event) {
event.preventDefault();
$.ajax({
url: '{% url "cart:remove_from_cart" %}',
type: "POST",
dataType: 'json',
data: {bookID: $('#idInput').val()},
success: function (response_data) {
alert('works fine')
},
error: function (response_data) {
console.log('error occurred');
}
});
});
});
</script>
{% for book in items_list %}
<div class="items">
<p id="title"> {{ book.book.title }}, quantity: {{ book.quantity }} </p>
<form method="post">
{% csrf_token %}
<input id="idInput" value="{{ book.book.id }}" >
<button id="remove" type="submit"> Remove</button>
</form>
</div>
{% endfor %}
The code in the function body below is just for testing. Once the first form works, I guess the problem was not caused by the function view.
cart/views.py:
#csrf_exempt
def remove_books(request):
cart = Cart.objects.get(user=request.user)
if request.method == 'POST':
passed_id = request.POST['bookID']
secured_id = int(passed_id)
response_data = {
'quantity': secured_id
}
return JsonResponse(response_data)
<script>
$(document).ready(function () {
$(".remove").click(function (event) {
// event.preventDefault(); // don't think it should be required with button type=button
var book_id = $(this).parent().find('.idInput').val(); // find correct input box.
var csrf = $('input[name="csrfmiddlewaretoken"]').val(); // get csrf token in variable.
// there are multiple ways to get csrf token, I personally like this ^^, see https://docs.djangoproject.com/en/2.1/ref/csrf/#ajax for more
$.ajax({
url: '{% url "cart:remove_from_cart" %}',
type: "POST",
dataType: 'json',
data: {
bookID: book_id,
csrfmiddlewaretoken: csrf // add csrf token to post data
},
success: function (response_data) {
alert('works fine')
},
error: function (response_data) {
console.log('error occurred');
}
});
});
});
</script>
{% csrf_token %} <!-- It will render a hidden input field with csrf token in it. Keep it outside for loop but in html. No need to render exactly same element multiple times. -->
{% for book in items_list %}
<div class="items">
<p class="title"> {{ book.book.title }}, quantity: {{ book.quantity }} </p>
<form method="post">
<input class="idInput" value="{{ book.book.id }}" > <!-- use class not id -->
<button class="remove" type="button"> Remove</button> <!-- you can use button type=button to avoid form submit and hence avoid event.preventDefault(); in js -->
</form>
</div>
{% endfor %}

Having trouble submitting my form using Dojo

I am using Django 1.5 and Dojo 1.8. I am trying to get Dojo to submit a form back to a Django view when I click a button.
Here is my Django view:
def report(request, report_id, report_url=None, template='report_parameters.html'):
if request.method == 'POST':
form = ReportParametersForm(request.POST)
if form.is_valid():
report_params = form.save()
html = "Success!"
return HttpResponse(html)
else:
form = ReportParametersForm()
return render(request,template, {
'form': form,
'report_url': report_url,
'report_id': report_id,
})
Here is the html page:
<div id="report_body">
<form data-dojo-type="dijit/form/Form" id="parameters_form" data-dojo-id="parameters_form">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<p><button id="submit_parameters" dojoType="dijit.form.Button" type="submit">Submit</button></p>
</form>
</div>
<script type="dojo/on" data-dojo-event="submit" data-dojo-args="e">
e.preventDefault();
require(["dojo/dom", "dojo/request", "dojo/dom-form"], function(dom, request, domForm){
on(dom.byId("submit_parameters"), "click", function() {
console.log("Dojo Post");
request.xhr("/report_parameters/report_id/report_url/", {
method: "post",
handleAs: "json",
data: domForm.toJson("parameters_form"),
}).then(
function(response){
alert(response);
dom.byId("report_body").innerHTML = "Report!";
},
function(error){
dom.byId("report_body").innerHTML = "<div class=\"error\">"+error+"<div>";
}
);
});
});
</script>
When I click the Submit button, I want to send a POST request to the url passing the data I have in my form. However, right now when I click Submit, the page reloads with a url looking something like this: /?csrfmiddlewaretoken=Y9gaNMFRWZNXMbJ2L3Ev7A5iKPGTuWeF&param_1=0&param2=0/report_parameters/report_id/report_url/.
I don't see the Dojo Post that should be appearing in my console.
How do I get my form to submit?
This fiddle seems to do what you want.
The major differences seem to be:
The <form> is actually a <div>. The Dojo documentation for Form links to reasons why this is done for IE.
All the related event script is inside the form <div>.
Remove the on(dom.byId("submit_parameters")... code, as there's already a declarative submit event handler.
HTML code:
<div id="report_body"></div>
<div data-dojo-type="dijit/form/Form" id="parameters_form" data-dojo-id="parameters_form" encType="multipart/form-data" action="" method="">
<input name="dummy" value="dummy">
<script type="dojo/on" data-dojo-event="submit" data-dojo-args="e">
console.log("submit");
e.preventDefault();
require(["dojo/dom", "dojo/request/xhr", "dojo/dom-form"], function(dom, xhr, domForm) {
console.log("Dojo Post");
var url = "/report_parameters/report_id/report_url/";
var data = domForm.toJson("parameters_form");
// overwrite url and data for jsfiddle
url = "/echo/json/";
data = {
json: data
};
xhr(url, {
method: "post",
handleAs: "json",
data: data,
}).then(function(response) {
alert(JSON.stringify(response, null, 2));
dom.byId("report_body").innerHTML = "Report!";
}, function(error) {
dom.byId("report_body").innerHTML = "<div class=\"error\">" + error + "<div>";
});
});
</script>
<button data-dojo-type="dijit/form/Button" id="submit_button" type="submit" name="submitButton" value="Submit">Submit</button>
</div>
JS code:
require(["dojo/parser", "dijit/registry", "dijit/form/Form", "dijit/form/Button", "dijit/form/ValidationTextBox", "dijit/form/DateTextBox", "dojo/domReady!"], function (parser, registry) {
parser.parse().then(function () {
console.log("parsed");
console.log(registry.byId("parameters_form"));
console.log(registry.byId("submit_button"));
});
});
I had to modify the above slightly. This is what eventually worked for me:
<div id="report_body"></div>
<form data-dojo-type="dijit/form/Form" id="parameters_form" data-dojo-id="parameters_form" encType="multipart/form-data" action="" method="POST">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<script type="dojo/on" data-dojo-event="submit" data-dojo-args="e">
e.preventDefault();
require(["dojo/dom", "dojo/request/xhr", "dojo/dom-form"], function(dom, xhr, domForm){
var url = "/report_parameters/report_id/report_url/"
var data = domForm.toObject("parameters_form")
xhr(url, {
method: "post",
data: data,
}).then(
function(response){
alert(response);
dom.byId("report_body").innerHTML = "Report!";
},
function(error){
dom.byId("report_body").innerHTML = error;
}
);
});
</script>
<p><button id="submit_parameters" dojoType="dijit/form/Button" type="submit" name="submitButton" value="Submit">Submit</button></p>
</form>
Using either the <div> or <form> tags to wrap the whole thing worked for me.