DataTables, serverSide and pagination on Django - django

I use DataTables with serverSide render.
<script>
$(document).ready( function () {
$('#myTable').DataTable({
"processing": true,
"serverSide": true,
"ajax": "{% url 'core:persons_json' %}",
"columns": [
{"data": "full_name"},
{"data": "email"},
]
});
});
</script>
In my views.py i have:
def persons_json(request):
persons = Person.objects.all()
data = [item.to_dict_json() for item in persons]
page = 1
per_page = 10
res = {
'data': data,
'page': page,
'per_page': per_page,
'total': math.ceil(persons.count() / per_page)
}
return JsonResponse(res)
But still he still returns all the persons. And I want to avoid loading a lot of data.
But when i define, for example:
def persons_json(request):
length = int(request.GET.get('length'))
persons = Person.objects.all()[:length]
data = [item.to_dict_json() for item in persons]
page = 1
per_page = 10
res = {
'data': data,
'page': page,
'per_page': per_page,
'total': math.ceil(persons.count() / per_page)
}
return JsonResponse(res)
length = int(request.GET.get('length')) is parameter send by serverSide:
http://localhost:8000/person/json/?draw=1&columns%5B0%5D%5Bdata%5D=full_name&columns%5B0%5D%5Bname%5D=&columns%5B0%5D%5Bsearchable%5D=true&columns%5B0%5D%5Borderable%5D=true&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B1%5D%5Bdata%5D=email&columns%5B1%5D%5Bname%5D=&columns%5B1%5D%5Bsearchable%5D=true&columns%5B1%5D%5Borderable%5D=true&columns%5B1%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B1%5D%5Bsearch%5D%5Bregex%5D=false&order%5B0%5D%5Bcolumn%5D=0&order%5B0%5D%5Bdir%5D=asc&start=0&length=10&search%5Bvalue%5D=&search%5Bregex%5D=false&_=1568072064631
Note that we have: start=0&length=10.
My question is as follows: when I use this second option. The pagination of other items does not appear in DataTables, ie
I wanted it here
but only one page appears.
Does anyone know how I do to return all pages, and I go clicking each to advance the pages?

I resolve. DataTable send start and length. And API need return recordsTotal and recordsFiltered.
https://github.com/rg3915/django-datatables-experiment/issues/1

Related

Save Django Model when list_editable fields are changed in Django Admin change_list view

I have a Django Model which I'm trying to update from the Admin change_list view when a field that is listed in list_editable has a change event fired. Right now the only way to save the updates is by clicking a "Save" button and then the page reloads. I don't want the page to reload at all. I just want it to update the model asynchronously.
For example I have the following model.
class Example(models.Model):
name = models.CharField()
hide = models.BooleanField(default=False)
In the Admin Form I have
class ExampleAdmin(admin.ModelAdmin):
list_display = [
"name",
"hide"
]
list_editable = [
"name",
"hide"
]
When viewing the change list for Examples now I will see the values listed out with an editable text input for name and a checkbox for hide.
I would like to listen for events on those inputs and when they are fired send an async request to update the specific model.
UPDATE
This is what I have right now.
window.addEventListener("load", function() {
(function($) {
$(".field-name .vTextField").on("change",function (event) {
$('#changelist-form').submit()
})
$('#changelist-form').submit(function(event) { // On form submit event
$.ajax({ // create an AJAX call...
data: $(this).serialize(), // get the form data
type: "POST", // GET or POST
url: $(this).attr('action'), // the file to call
success: function(response) { // on success..
console.log(response)
},
error: function(e, x, r) { // on error..
console.log(e)
}
});
event.preventDefault()
return false;
});
})(django.jQuery);
})
I can see this firing in the network requests and can verify that the serialized data being sent is updated. The response is a 200, but when I refresh the page the data has not been updated.
UPDATE
At this point I'm giving up on sending this data to Django and will be sending the serialized data to a custom endpoint and parse through and save the data in a custom save function.
Within your Admin Model add the following
class ExampleAdmin(admin.ModelAdmin):
class Media:
js = ("js/admin/example.js")
list_display = [
"name",
"hide"
]
list_editable = [
"name",
"hide"
]
example.js
window.addEventListener("load", function() {
(function($) {
$(".field-name .vTextField").on("change",function (event) {
submit()
})
function getFormData($form){
var unindexed_array = $form.serializeArray();
var indexed_array = {};
$.map(unindexed_array, function(n, i){
indexed_array[n['name']] = n['value'];
});
return indexed_array;
}
function submit(){
$.ajax({
data: getFormData($('#changelist-form')),
type: "POST",
url: '/api/save-example',
success: function(response) {
console.log(response)
},
error: function(e, x, r) {
console.log(e)
}
});
}
})(django.jQuery);
})
I'm using Django Rest Framework so I created a POST endpoint, but you could also do this in a Django View that is listening for a POST.
When you execute the post Django will send the data categorized by the row number and field name, like so.
form-0-id : 1
form-0-name: example-name
form-0-hide: false
form-0-example_parent: 100
form-1-id : 2
form-1-name: example-name-two
form-1-hide: true
form-1-example_parent: 200
#api_view(["POST"])
def save_example(request):
try:
total_rows = int(request.POST.get("form-TOTAL_FORMS"))
prefix = "form-"
form_fields = {
"id": "id",
"name": "name",
"hide": "hide",
"example_parent": "example_parent_id",
}
for x in range(total_rows):
item = {}
for key, val in form_fields.items():
post_field = prefix + str(x) + "-" + key
field = val
if key == "id":
id = request.POST.get(post_field)
else:
item[field] = request.POST.get(post_field)
Example.objects.filter(id=id).update(**item)
return Response(status=status.HTTP_200_OK)
except Exception as e:
return Response(status=status.HTTP_404_NOT_FOUND)
Note
total_rows = int(request.POST.get("form-TOTAL_FORMS")) which lets you know how to loop through all the form data that is passed back.
This doesn't take different values into consideration. (i.e. Checkboxes will return an on value but no value if not checked.
You will need to update your save_example function accordingly.

Django Server Side set-up for pagination with Datatable

I am using Django for backend, and I am using Datatable library to display a large number of records ( approx 1 million records ). I am trying to set up datatable in such a way that
every time 25 records are being fetched from the backend and when the user clicks on the next page button, another ajax call gets the next 25 records and so on.
But I am having a trouble setting these up.
My DataTable initialisation :
$("#company-table").DataTable({
"processing": true,
"serverSide": true,
"bDestroy": true,
ajax: {
type: "POST",
url: "/get_results/",
headers: {
"X-CSRFToken": getCookie("csrftoken"),
},
data: {
...other data params...
page:$("#company-table").DataTable().page()
},
},
columns: [
...populating columns...
],
});
And my views.py looks like this ( It is completely wrong as far as I know ) :
#filtered_queryset contains all the records.
paginator = Paginator(filtered_queryset, 25) # Show 25 contacts per page.
page_number = request.POST.get('page')
start = request.POST.get('start')
length = request.POST.get('length')
page_obj = paginator.get_page(page_number)
data = list(page_obj.object_list.values())
return_data = {"data": data}
json_data = json.dumps(return_data,indent=4, sort_keys=True, default=str)
return HttpResponse (json_data, content_type = "application/json")
Can anyone help me? Or just nudge me in the right direction?
Try this one django-ajax-datatable
I hope this will work

Multiple Django subforms displayed with ajax in a main form but data not registered when subforms are submited (without error)

EDIT #1
well maybe because it is not clear in my head
and your are right saying Django forms are not intented to be used with ajax that why I wonder if there is way way doing what I want using Django forms without losing power of Django forms validation
I will try to clear my demand
considering this UI below
When user reach this "patient page" = main form (image 1), he can select "Inclusion form = subform (INCL-001 for instance) in the menu
I want the corresponding form to be displayed without refresh the page ; that why I have used ajax to display the corresponding form
If the corresponding form does not exist, it is a CREATION and a blank "Inclusion form" is displayed : user can enter data and save
If the corresponding form already exist, it is a UPDATE and a prefilled "Inclusion form" is displayed : user can update data and save or delete the form
what would be an elegant way of doing that?
I have a main form (patient.html) and I want to display different subforms (inclusion.html for instance) with ajax in this form.
I manage to display a subform when user click on a button in the main form
But, data are not stored when submitted and it is quite normal as no view is linked to this subform
I have read about this in different post but I miss the concept how to...
I thinks it is a common task with web so probably Django as an easy way to do it?
thanks for advices
urls.py
app_name='ecrf'
urlpatterns = [
path('', views.index, name='index'),
path('patient/<pk>', views.patient, name='patient'),
path('sub_form', views.sub_form, name='sub_form'),
]
views.py
def patient(request,pk):
patient = {"id":1,"num":'A-001',"dat":'Mon 11, novembre 2020',"arm":1,"doc":'Slater'}
return render(request, 'ecrf/patient.html', {'patient':patient})
def sub_form(request):
if request.is_ajax():
return JsonResponse(
{
'html_inclusion': render_to_string(
'ecrf/inclusion.html',
{
'form': InclusionForm,
'language': request.session.get('language')
},
request=request
),
},
status=200
)
js
$(document).ready(function () {
...
$("#add").on("click",function (event) {
var csrftoken = getCookie('csrftoken');
$.ajax({
type: "POST",
url: '/ecrf/sub_form',
data: {
csrfmiddlewaretoken: csrftoken,
},
dataType: 'html',
success: function (data) {
$("#sub_form").children().remove();
obj = JSON.parse(data);
$("#sub_form").append(obj.html_inclusion);
},
error: function (data) {
console.log('error');
}
});
});
$("body")
.on("click", '#inclusion', function (event) {
console.log('inclusion click')
})
});

Show loading gif until the django view performs the data processing and renders the template with this data

I have a django project where the page has multiple nav links representing different agents. On clicking any nav link, the urls.py redirects to nav specific view and the view needs to perform some processing to get the data needed to render the template. However as this is syncrhonous rendering it takes a long while to load data (in the order of 15-20s).
Below is my urls.py:
from django.urls import path
from . import views
app_name = 'agent'
urlpatterns = [
path('agent1/', views.agent1, name='agent1'),
path('agent2/', views.agent2, name='agent2'),
path('agent3/', views.agent3, name='agent3'),
path('agent4/', views.agent4, name='agent4'),
]
My views method looks as below:
def agent1(request):
agent_data = Agent1.objects.all()
agent_details = get_agent_details(agent_data)
return render(request, 'manager/data.html', {'agent_data': agent_data, 'agent_details': agent_details})
I am using the {{ agent_data.name }}, {{ agent_data.code }}, {{ agent_data.qty }} and {{ agent_data.price }} along with data from agent_details dictionary in my html to populate a table's rows. How should I change my view method, so that it loads the data via AJAX (javascript) in order to show a loading gif in the meantime and also provide me the data so that I can populate the table. Could someone help me with the Ajax code and the steps as I am new to this technology and not finding any help going through the online tutorials.
So for this to work with ajax, you'll need some javascript in manager/data.html which knows the url to fetch data from.
As an example, I've got an ajax setup which checks a given email address isn't already in use;
(function($) {
$(document).ready(function() {
var validateEmailURL = $section_user_signup.data('ajax-email-url');
function validateEmailUnique() {
var valid = true;
clearError($email);
// Fetch unique status of the provided email
$.ajax({
async: false,
url: validateEmailURL,
method: 'POST',
type: 'POST',
dataType: 'json',
data: {
'email': $email.val(),
'csrftoken': $form.find('input[name="csrfmiddlewaretoken"]').val()
},
success: function (response) {
valid = true;
},
error: function (response) {
setError($email, response["responseJSON"]["error"]);
valid = false;
}
});
return valid;
}
});
})(window.jQuery);
This javascript uses the data attribute of a div for the URL to check;
<div data-ajax-email-url="{% url 'account_ajax_validate_email' %}">
The view which the ajax call goes to looks like this;
def ajax_check_email_unique(request, *args, **kwargs):
"""
Return an JsonResponse to identify if an email is unique.
"""
if not request.is_ajax():
return HttpResponseBadRequest()
if request.is_ajax and request.method == "POST":
email = request.POST.get('email')
if email_address_exists(email):
return JsonResponse(
{
"error":
"Email address already exists. Click "
f"here "
"to login"
},
status=400
)
return JsonResponse(
{"email": email},
status=200
)
# some error occurred
return JsonResponse({"error": ""}, status=400)
The important thing for any view which will be used by javascript is that you return a JsonResponse.
So if I was you, I'd setup a new view for ajax, and that makes your existing one really simple;
def agent1_ajax(request):
agent_data = Agent1.objects.all()
agent_details = get_agent_details(agent_data)
return JsonResponse({
"agent_data": agent_data, "agent_details": agent_details
}, status=200)
def agent1(request):
return render(request, 'manager/data.html', {})
And as far as a loading gif goes, you'd need an element that contains the gif and then you can bind to the ajax event to show/hide;
$(document).ajaxStart(function() {
$("#loading").show();
});
$(document).ajaxStop(function() {
$("#loading").hide();
});

Updating only part of an HTML page using Django

I have an HTML (annotator.html) page where the top half contains a table. I have JavaScript code that pulls data out of the data model when the user selects a row in the table and displays that data in a div below the table.
My Javascript looks like this:
$('#radiological tbody').on( 'click', 'tr', function () {
if ( $(this).hasClass('selected') ) {
$(this).removeClass('selected');
}
else {
//first unselect any other selected row
dt_table.$('tr.selected').removeClass('selected');
//then select row of interest
$(this).addClass('selected');
var rowIndex = dt_table.row('.selected')[0];
var row = dt_table.rows(rowIndex);
var id = row.data()[0]['id'];
var url = '/report/' + id + '/';
//window.location = url;
$.ajax({
url: url,
type: 'get', // This is the default though, you don't actually need to always mention it
success: function(data) {
},
failure: function(data) {
alert('Got an error dude');
}
});
And correctly populates the bottom half of my page but also updates the top half. I Just want the bottom half refreshed.
My report view looks like this:
def report_page(request, id):
template_name = "annotator.html"
data = Radiology2.objects.get(id=id)
context = {"data": data}
context["title"] = title
context["version"] = version
context["id"] = id
if (request.is_ajax()):
# return render(request, template_name, context)
return JsonResponse(context)
else:
return render(request, template_name, context)
I added support for AJAX. I'm just not sure how to return from view properly when AJAX is used. Obviously, I'm not understanding something.
Instead of Ajax, I replaced window.location=url; with $('#divIdToRefresh').load(url + ' #divIdToRefresh');
You can easily use Ajax - jQuery or native JS would be better for that