JQuery's DataTables Editor plugin and Django - django

Integrating the DataTables plugin was straightforward and getting the Editor add-on integrated was also pretty painless - up to a point. However, the client/server side has been a bear for me.
The following is the JavaScript for DataTables and Editor. The part I cannot resolve is this snippet from the code that follows
var table = $('#theader').DataTable( {
**bProcessing: true,
bServerSide: true,
start: 1,
dataSrc: "id",
sAjaxSource: 'load_user_json',**
}
After the JavaScript is the html code relevant to the JavaScript.
The examples given, on the DataTables/Editor site, use PHP on the server side. I know zero about PHP and I cannot figure out how to replace it with Python to pass back JSON (via the code snippet above) to the Javascript that follows using Ajax which is a current requirement of the DataTables plugin.
Everything looks great. Everything work except getting the new/edit/delete action to work. I started with the following example on the DataTables/Editor site.
https://editor.datatables.net/examples/styling/bootstrap.html
JAVASCRIPT (DataTables / Editor)
$(document).ready(function() {
$(".dropdown-toggle").dropdown();
});
$(document).ready(function() {
$(".dropdown-toggle").dropdown();
});
$(document).ready(function edit_users() {
var csrftoken = getCookie('csrftoken');
var editorUser = new $.fn.dataTable.Editor( {
ajax: '',
table: "#theader",
fields: [ {
label: "ID:",
name: "ID"
}, {
label: "Name:",
name: "NAME"
}, {
label: "CM:",
name: "CM"
}, {
label: "Email:",
name: "EMAIL"
} ]
} );
if ( !$.fn.dataTable.isDataTable( '#theader' ) ) {
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
var table = $('#theader').DataTable( {
bProcessing: true,
bServerSide: true,
start: 1,
dataSrc: "id",
sAjaxSource: 'load_user_json',
columns: [
{ data: "ID" },
{ data: "NAME" },
{ data: "CM" },
{ data: "EMAIL" }
],
select: true
} );
}
new $.fn.dataTable.Buttons( table, [
{ extend: "create", editor: editorUser },
{ extend: "edit", editor: editorUser },
{ extend: "remove", editor: editorUser }
] );
table.buttons().container()
.appendTo( $('.col-sm-6:eq(0)', table.table().container() ) );
$('#theader tfoot th').each( function () {
var title = $(this).text();
$(this).html( '<input type="text" placeholder="Search '+title+'" />' );
} );
table.columns().every( function () {
var that = this;
$('input', this.footer() ).on( 'keyup change', function () {
if ( that.search() !== this.value ) {
that
.search( this.value )
.draw();
}
} );
} );
} );
HTML
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load staticfiles %}
{% block content %}
{% if queryset %}
<h2>Current Users</h2>
<table id="theader" class="table table-bordered table-hover small order-column">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>CM</th>
<th>EMAIL</th>
</tr>
</thead>
<tfoot>
<tr>
<th>ID</th>
<th>NAME</th>
<th>CM</th>
<th>EMAIL</th>
</tr>
</tfoot>
<tbody>
{% for row in queryset %}
<tr id=forloop.counter> <!-- Needed for DataTables row identification -->
<td>{{ row.operator_id }}</td>
<td>{{ row.fullname }}</td>
<td>{{ row.cm }}</td>
<td>{{ row.email }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<h4>No SKUs have been defined</h4>
{% endif %}
<script src="{% static 'js/searchable-users-table.js' %}"></script>
{% endblock %}

First things first; you don't have to provide a JSON endpoint to work with DataTables.js. You can render a table and call DataTable() on it.
Take the following example:
$(document).ready(function(){
$('#theader').DataTable({
pageLength:25,
rowReorder:false,
colReorder:true,
order: [[1, 'asc'],[0, 'asc']]
});
});
The table element at id theader is passed to DataTable, where the magic happens; DataTables will paginate, order, and allow for re-ordering based on this. If you don't know how to build a JSON endpoint, you can avoid it for now unless you truly need to have in-table editing.
If you do want to explore building a JSON API, Django Rest Framework is a great option and allows for fine control over serialization of models. This means that you can use the modular Viewsets and Serializers from DRF to build out all CRUD functionality for a given model / set of related models at a single endpoint.
However, for quick and dirty retrieval-only applications you can also build a view, call it via JS/JQuery's AJAX function on page load, and return a JsonResponse in your Django view. It's very quick and basically boils down to this:
Retrieve a queryset for a model based on some parameters (either provided via request.GET, request.POST, or url parameters)
Process as needed.
Convert an array of values (my_queryset=SomeModel.objects.filter(something=somevalue).values('field_1','field2'))
Serialize to JSON and respond, setting safe=False in the JsonResponse kwargs if not return a dictionary.(return JsonResponse(my_queryset, safe=False)). Alternately, convert your queryset to an ordered dict and pass it to JsonResponse.

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>

Issues with csfr token and ajax call

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

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: Using DataTables

I want to introduce filtering and sorting functionality to my table using ajax. From what I found, DataTables seamed to be the best option. but, I can't seem to get it working! The first thing I tried was using it just how they have it set up with there demos. But I could not seam to get the search form to generate and the sorting wont work either. I then tried to use one of the many packages created to implement that functionality. However, I found that the documentation was usually not very clear and difficult to follow, or even when I did follow it, I would be left with the table rendering fine, but the search and sort would still not be available. So I've decided to go back to my original and see if someone might know what I'm doing wrong. The page does render the table correctly, and if I view page source, the javascript is properly linked.
Here is the html:
<pre>
<code>
{% extends "theme_bootstrap/base.html" %}
{% load staticfiles %}
{% block extra_style %}
<script src="{% static "js/jquery.js" %}"></script>
<script src="{% static "js/jquery.dataTables.js" %}"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
$('#test').dataTable();
});
</script>
{% endblock %}
{% block body %}
{{ store.StoreName}}
{% if liquors.count > 0 %}
<h1>liquors</h1>
<table id="test">
<thead>
<tr>
<th>Liquor Code</th>
<th>Brand Name</th>
<th>Vendor Name</th>
</tr>
</thead>
<tbody>
{% for liquor in liquors %}
<tr>
<td>{{ liquor.LiquorCode }}</td>
<td>{{ liquor.BrandName }}</td>
<td>{{ liquor.VendorName }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>None to show!</p>
{% endif %}
{% endblock %}
</code>
</pre>
Here is also my view. Perhaps I've done something wrong here.
def liquors(request, store_id):
args = {}
args.update(csrf(request))
a = Store.objects.get(StoreID=store_id)
args['liquors'] = Liquor.objects.all()
args['a'] = a
return render(request, 'liquors.html', args)
I did this on a project I worked on a while back. Here's how I defined the table in my template:
$(document).ready( function () {
var ticketTable = $('#ticket-table').dataTable( {
"fnDrawCallback": function() {
// Initialize popovers anytime new data is loaded into the table
$('a[rel=tooltip]').tooltip({
placement: 'left'
});
},
"bServerSide": true,
"bAutoWidth": false,
"sPaginationType": "bootstrap",
"sAjaxSource": "{% url get_tickets_list %}",
"aaSorting": [[ 2, "desc" ]],
"iPageLength": 25,
"bLengthChange": false,
"bStateSave": true,
"bFilter": true,
"sDom": "<'length-change'><'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'length-change'><'span6'p>>",
"oLanguage": {
"sSearch": ""
},
"aoColumns": [
{ "bSortable": false, "sWidth": "14px", "bSearchable": false },
{ "sWidth": "160px", "bSearchable": true },
{ "sWidth": "60px", "bSearchable": true },
{ "bSearchable": true },
{ "bSortable": false, "sWidth": "14px", "bSearchable": false },
{ "sWidth": "50px", "sClass": "center", "bSearchable": false },
{ "bSearchable": true },
{ "sWidth": "70px", "sClass": "center", "bSearchable": false },
{ "sWidth": "75px", "bSearchable": true }
] } ).fnSetFilteringDelay(500);
Key is this line in the table options which defines the source URL for the AJAX request from the DataTable:
"sAjaxSource": "{% url get_tickets_list %}",
Then, you'll also need a view to return the AJAX requests:
def get_tickets_list(request, queryset=Ticket.objects.all()):
"""
AJAX calls for DataTable ticket list.
"""
#columnIndexNameMap is required for correct sorting behavior
columnIndexNameMap = { 0: 'link', 1 : 'created', 2: 'priority', 3: 'client', 4: 'client_email', 5: 'sites', 6: 'problem',7: 'updates', 8: 'state' }
#path to template used to generate json (optional)
jsonTemplatePath = 'json_tickets.txt'
#call to generic function from utils
return get_datatables_records(request, queryset, columnIndexNameMap, jsonTemplatePath)
As I said, this was a while ago and may not work anymore with the latest versions of DataTable, but maybe it will set you in the right direction.

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.