Issues with csfr token and ajax call - django

I tried to implement a button on a Website, which upon pressing it automatically runs a python function on the server and copies some value into the clipboard of the user. The clipboard copying runs fine, but I can not run the python function.
Whenever I try to I get an error 403 and I think it is due to an issue with the csfr token. Can anyone help me to solve this issue?
Here is my HTML
{% if categories %}
<div class="card shadow mb-4">
<div class="card-body card-interface">
<table id="predictionTable" class="table table-bordered">
<thead>
<tr>
<th>Vorhersage</th>
<th>Wahrscheinlichkeit</th>
<th>Kopieren</th>
</tr>
</thead>
<tbody>
{% for category in categories %}
<tr>
<td>{{ category.0}}</td>
<td>{{ category.1}}%</td>
<td><img src="{% static "documents/img/copy.png" %}" class="interface-copy" value="{{ category.0 }}" input_text = "{{ input_text }}" style="cursor: pointer"></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div id="django-data" data-CSRF="{{csrf_token}}"></div>
And here the .js that is run
$(".interface-copy").on('click', function(e) {
var csrftoken = $("django-data").data().CSRF;
console.log(csrftoken);
console.log("test")
var $temp = $("<input>");
$("body").append($temp);
$temp.val($(this).attr('value')).select();
document.execCommand("copy");
$temp.remove();
console.log("test")
$.ajax({
url: "/ajax/postSingleSourceEntry/",
type : 'POST',
beforeSend: function(request){
request.setRequestHeader("X-CSRFToken", csrftoken);
},
data: {
csfrmiddlewaretoken: csrftoken
},
dataType: "json",
success: function (data){
console.log("call created")
},
error : function(response){
console.log(response)
}
})
});

Change:
<div id="django-data" data-CSRF="{{csrf_token}}"></div>
To:
<div id="django-data" data-csrf="{{csrf_token}}"></div>
And:
var csrftoken = $("django-data").data().CSRF;
To:
var csrftoken = $("#django-data").data().csrf; // Note the # before django-data and csrf in small letter.
You might want to read: How to get the data-id attribute?

You can add #csrf_exempt decorator for that ajax function

Related

Django template data update using ajax

I am trying to update my data periodically (10 seconds) on a Django template using ajax scripts. I am relatively new in front-end development.
Using other articles, I am able to do so. But everytime page refreshes, multiple threads for page refreshing are created and update requests are doubled every 10 secods.
Following is my django template snippet:
<body id="page-top">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr class="table-info">
<th style="text-align:center">Parameter</th>
<th style="text-align:center">A</th>
<th style="text-align:center">B</th>
</tr>
</thead>
<tbody>
{% for para, details in manualData.items %}
<tr>
<th style="text-align:center" scope="row">{{ details.1.name }}</th>
{% for key, entity in details.items %}
<td style="text-align:center">
<font color="white" size="4px">{{ entity.value }}</font>
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
I am using ajax script as follows:
<script type="text/javascript">
function refresh() {
var_ref = $.ajax({
success: function (data) {
$('#page-top').html(data);
}
});
}
$(function () {
setInterval('refresh()', 10000);
});
</script>
Conclusively, all I need is:
once the refreshing process is called, new process should not be
created, or else past process to be aborted if new process is to be
defined.
Kindly help me to attain the same.
Thanks in advance
Nakul
You need your ajax call to be synchronomous instead of async, this way you will block the thread until you get the data you require
Can be done by adding the corresponding attribute on the ajax call
When within the industry you would normally use the template system to load something like react, angular or vue which constantly update the DOM without having to make endless polling
Thanks to the answer at post: Refresh page periodically using jquery, ajax and django.
Also, Thank you #Mr-Programs for your suggestion regarding async attribute.
I have got a suitable answer for my query and updated the javascript as follows.
<script>
function worker(){
var url = 'yoururl';
$.ajax({
type:'get',
url: url,
async: false,
success: function(data) {
var dtr = $('.page-top',data);
# not necessarily body div, can use any section required to be updated
$('.page-top').html(dtr);
complete: function() {
setTimeout(worker, 10000);
}
});
}
$(document).ready(function(){
setTimeout(worker, 10000);
});
};
</script>

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 %}

Vue.js For loop is not rendering content

I am building a web application using vue.js, vue-resource, vue-mdl and google material design lite.
JS compilation is performed using webpack through laravel elixir.
In this app I have to render a table row for each object from an array returned from a Rest API (Django Rest Framework). I have made the following code inside the html to render content using vue.js:
<tr v-for="customer in customers">
<td class="mdl-data-table__cell--non-numeric">{{ customer.status }}</td>
<td class="mdl-data-table__cell--non-numeric">{{ customer.name }}</td>
<td class="mdl-data-table__cell--non-numeric">{{ customer.company }}</td>
<tr>
This should render all objects in the array as a table row. I have also tried to wrap the above in a template tag like this:
<template v-for="customer in customers">
<tr>
<td class="mdl-data-table__cell--non-numeric">{{ customer.status }}</td>
<td class="mdl-data-table__cell--non-numeric">{{ customer.name }}</td>
<td class="mdl-data-table__cell--non-numeric">{{ customer.company }}</td>
</tr>
</template>
This did not change either.
I have also tried to hardcode the array inside the ready() function of the vue instance, but this did not help either.
window._ = require('lodash');
require('material-design-lite');
window.Vue = require('vue');
require('vue-resource');
var VueMdl = require('vue-mdl');
Vue.use(VueMdl.default);
const app = new Vue({
el:'body',
ready: function(){
//this.getCustomerList();
this.customers = [
{ name: "Peter", status: "true", company: "Company 1"},
{ name: "John", status: "false", company: "Company 2"}
]
},
data: {
customers: [],
response: null,
messages: []
},
methods: {
getCustomerList: function()
{
this.$http({url: '/api/customers/', method: 'GET'}).then(function(response){
//Success
this.customers = response.data
console.log(response.data)
},
function(response){
console.log(response)
})
}
}
})
Changing the above to this does not change either:
window._ = require('lodash');
require('material-design-lite');
window.Vue = require('vue');
require('vue-resource');
var VueMdl = require('vue-mdl');
Vue.use(VueMdl.default);
const app = new Vue({
el:'body',
ready: function(){
//this.getCustomerList();
},
data: {
customers: [
{ name: "Peter", status: "true", company: "Company 1"},
{ name: "John", status: "false", company: "Company 2"}
],
response: null,
messages: []
},
methods: {
getCustomerList: function()
{
this.$http({url: '/api/customers/', method: 'GET'}).then(function(response){
//Success
this.customers = response.data
console.log(response.data)
},
function(response){
console.log(response)
})
}
}
})
I have also tried to just make a plain html table that does not have any of the Google MDL classes applied, and this does also not give any result.
Logging this.customers to the console shows that it does in fact contain the data, but for reason it is not rendering. Why is that? What am I doing wrong?
Here's a snippet of your code, which works as expected. I've added in CDN references to the libraries you mentioned, but I'm not doing anything with them. I offer this as a starting point for you to see if you can find what changes will reproduce your problem here.
const app = new Vue({
el: 'body',
ready: function() {
//this.getCustomerList();
this.customers = [{
name: "Peter",
status: "true",
company: "Company 1"
}, {
name: "John",
status: "false",
company: "Company 2"
}]
},
data: {
customers: [],
response: null,
messages: []
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/vue-resource/0.9.3/vue-resource.min.js"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://code.getmdl.io/1.2.0/material.indigo-pink.min.css">
<script defer src="https://code.getmdl.io/1.2.0/material.min.js"></script>
<script src="https://rawgit.com/posva/vue-mdl/develop/dist/vue-mdl.min.js"></script>
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--3-col">
<button id="create-customer" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect" #click="$refs.createCustomer.open">
Create customer
</button>
</div>
<div class="mdl-cell mdl-cell--3-col">
<button id="convert-reseller" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect">
Convert to reseller
</button>
</div>
<div class="mdl-cell mdl-cell--3-col">
<button id="remove-customer" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect">
Remove Customer
</button>
</div>
<div class="mdl-cell mdl-cell--3-col">
<button id="change-status" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect">
Change status
</button>
</div>
</div>
<div class="mdl-grid">
<div class="mdl-cell mdl-cell--12-col">
<table class="mdl-data-table mdl-js-data-table mdl-data-table--selectable" style="width:100%;">
<thead>
<tr>
<th class="mdl-data-table__cell--non-numeric">Status</th>
<th class="mdl-data-table__cell--non-numeric">Customer name</th>
<th class="mdl-data-table__cell--non-numeric">Company</th>
</tr>
</thead>
<tbody>
<tr v-for="customer in customers">
<td class="mdl-data-table__cell--non-numeric">{{ customer.status }}</td>
<td class="mdl-data-table__cell--non-numeric">{{ customer.name }}</td>
<td class="mdl-data-table__cell--non-numeric">{{ customer.company }}</td>
</tr>
</tbody>
</table>
</div>
</div>
It seems now to be working.
Inside app.js I had:
const app = new Vue({ el: 'body' })
That, for some reason, conflicted with the one I was creating inside customer_list.js—although my methods worked fine.

Django links in Knockout Table

I have read this post: How can I create a text link in a Knockout javascript table? along with a couple others.
But, I am missing something somewhere, or not taking the right approach. I've included the relevant chunks of code for my problem. I am trying to use the table generated by knockout to to either update a task or remove a task. The remove part is working fine. I am trying to get the update to link to another page that is used to update the task. I cannot figure out what I need to do to get the link working properly in the update column.
I've tried several different approaches for how to put the url in the list of dictionaries that is passed to the KO model. Any advice to steer me in the right direction? If I am missing any information, please let me know. Thank you.
Views.py
def TaskList(request, job_id):
job_tasks = Tasks.objects.filter(parent=job_id)
tasks_list = []
for task in job_tasks:
task_row = {}
task_row['task_id'] = task.task_id
task_row['t_name'] = task.name
task_row['date'] = task.date_created
task_row['state'] = task.state
task_row['url'] = '{% url tracking:update_task task_id=task.task_id %}'
tasks_list.append(task_row)
json_tasks = json.dumps(tasks_list)
if request.POST:
json_data = request.POST.get('json_blob')
obj = loads(json_data)
task.task_id = obj.get("task_id")
remove_task = Tasks.objects.get(task_id=task.task_id)
remove_task.delete()
messages.success(request, 'Task removed')
HTML
<table>
<thead>
<th>Name</th>
<th>Date</th>
<th>State</th>
<th>Update</th>
<th>Remove</th>
</thead>
<tbody data-bind "foreach: tasks">
<tr>
<td data-bind="text: t_name"></td>
<td data-bind="text: date"></td>
<td data-bind="text: state"></td>
<td a class="btn" data-bind="attr: {href: url}">Update</a></td>
<td button class="btn" data-bind="click: $root.remove_task">Remove</button></td>
</tr>
</tbody>
</table>
{% block javascript_variables_nocompress %}
window.TASKS = {{ json_tasks|safe }};
{% endblock %}
{% block javascript_compress %}
<script type='text/javascript' src="{% static 'js/knockout/knockout.js' %}"></script>
<script type="text/javascript">
$(function() {
var RemoveTaskModel = function () {
var self = this;
self.tasks = ko.observableArray(window.TASKS);
self.remove_task = function(task) {
self.tasks.remove(task);
$("#json_blob").val(ko.toJSON(task));
}
}
ko.applyBinding(new RemoveTaskModel());
});
</script>
{% endblock %}
HTML
I would use reverse to do a reverse lookup of the URL for each task:
from django.core.urlresolvers import reverse
def TaskList(request, job_id):
job_tasks = Tasks.objects.filter(parent=job_id)
tasks_list = []
for task in job_tasks:
...
task_row['url'] = reverse('update_task', args=(),
kwargs={'task_id': task_id})
Then your observableArray should be able to bind the property from the JSON to the anchor tag. You might also note that in your code sample, your td is malformed:
<td a class="btn" data-bind="attr: {href: url}">Update</a></td>
it should be:
<td><a class="btn" data-bind="attr: {href: url}">Update</a></td>

handling ajax and jquery in django template

I am trying to load page in two parts.
second part is only render when user click on 'show more details'
<script>
$(document).ready(function(){
$('#toggle_details').click(function(e){
e.preventDefault();
if ($(this).hasClass('up')){
$(this).removeClass('up').addClass('down');
$('#toggle_text').html('Show More Details');
}
else {
$(this).removeClass('down').addClass('up');
$.ajax({
url: 'some_url_returning_json',
data: $(this).serialize(),
processData: false,
dataType: "json",
success: function(data) {
$( '.name' ).html(data.name);
$( '.lname' ).html(data.lname);
alert(data.name);
}
})
$('#toggle_text').html('Hide Details');
}
$('#details').slideToggle("slow");
return false;
});
$('#details').hide();
});
</script>
and my html is :
<div class="ad-grp-tbl creative-tbl custom-tbl">
<table width="100%">
<tr>
<th>Status:</th>
<td id='status'>{{ status }}</td>
</tr>
</table>
<table width="100%" id="details">
<tr>
<th>Name:</th>
<td id="name" >{{data.name}}</td>
</tr>
<tr>
<th>Last Name:</th>
<td id ="lname">{{ data.lname}}</td>
</tr>
</table>
<table>
<tr>
<th class="tog">
<span id="toggle_text" style="color:blue;font-weight:bold">Show More Details</span>
<span class="down" id="toggle_details"></span>
</th>
<td></td>
</tr>
</table>
</div>
So Basically I am not able to load the json return value in the template.
hw can i fix it. or my approach for solving the problem is wrong.
Thanks.
I show you an example:
def post_ajax(request):
TOTLE = 5
OFFSET = int(request.GET.get('offset', 0))
END = OFFSET + TOTLE
if OFFSET + 1 >= Post.objects.count():
LOADED = "已经全部加载完毕"
return HttpResponse(LOADED)
posts = Post.objects.filter(pub_time__lte=timezone.now())[OFFSET:END]
json_list = []
for post in posts:
t = get_template('blog/ajax_post.html')
html = t.render(Context({'post': post}))
# print(html)
json_list.append({
'html': html,
})
data = json.dumps(json_list)
return HttpResponse(data, content_type="application/json")
Is this you need?
Ajax + JQuery will get response and should put data appropriately in the page. Template of original page doesn't have much role to play.
However, you have to implement separate url+view+template that will handle the ajax request. You can use existing view but need to handle for ajax request (i.e. just to send part of html, likely using another template).
The template for ajax response should send only the relevant part of html and not the entire html page.
In the HTML you have ids set but you are using the class selector.
It should be:
$( '#name' ).html(data.name);
$( '#lname' ).html(data.lname);
instead of:
$( '.name' ).html(data.name);
$( '.lname' ).html(data.lname);
. is the class selector and # is the id selector.
You can try using Firebug or Chrome Dev Tools to see that the above returns the items.