Mostrar los datos de db en una tabla, una fila/objeto a la vez con Ajax python django

Estoy trabajando en un proyecto de Django donde muestro una lista de datos. Cuando la lista es demasiado larga, la página tarda más en cargarse debido a la cantidad de datos que se muestran en esa lista.

En lugar de tener un Django for loop para obtener todos los datos primero y luego mostrar la página, estoy pensando en mostrar primero la página y luego cargar todos los datos de la base de datos a través de ajax en segundo plano.

Para hacer eso, estaba pensando en mostrar primero la plantilla completa sin los datos de los objetos para cargar la página rápidamente y luego usar una llamada Ajax para obtener los datos con una función de vista, un objeto a la vez.

¿Es esto viable y cómo puedo hacerlo?

Mi datalist.py:

from __future__ import absolute_import
from __future__ import print_function
from django.views.generic import ListView
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required, user_passes_test
from django.urls import reverse_lazy , reverse
from django.apps import apps

from django.http import HttpResponse , HttpResponseRedirect , Http404
from .models import *
from .forms import *
from .common import *

from django.http import JsonResponse

import json
from django.forms.models import model_to_dict
from django.core import serializers
from django.core.serializers.json import DjangoJSONEncoder

class DataList(ListView):
    @method_decorator(login_required)
    def dispatch(self,request, *args, **kwargs):
        self.app = ''
        self.app1 = self.request.GET.get('app',None)
        if  self.app1 != None:
            self.app = self.app1
        else:
            self.app = eval(self.kwargs['table'])._meta.app_label
            print((self.app))
        self.model = apps.get_model(app_label=self.app, model_name=self.kwargs['table'])
        self.template_name = "{0}/{1}/list.html".format(self.app,self.kwargs['table'])
        request.session['url'] = reverse_lazy('{0}:data-list'.format(self.app), kwargs={'is_list':0,'app':self.app,'table':self.kwargs['table']})
        self.name = eval(self.kwargs['table'])._meta.verbose_name_plural
        request.session['title_page'] = self.name
        return super(DataList, self).dispatch(request,*args, **kwargs)

    def get_queryset(self):
        if self.kwargs['table'] == 'Myprotocol':
            protocoltype = self.request.GET.get('protocoltype')
            mytype = self.request.GET.get('type')

            queryset = self.model.objects.all()

        return queryset

    def get_context_data(self, **kwargs):
        context = super(DataList, self).get_context_data(**kwargs)
        context['page_title'] = self.kwargs['table'] + ' list'
        context['app_name'] = self.app
        context['table'] = self.kwargs['table']
        context['mybase'] = "{0}/base.html".format(self.app)
        context['form_name'] = eval('{0}Form'.format(self.kwargs['table']))


        context['add_perm'] = self.request.user.has_perm("{0}.add_{1}".format(self.app,self.kwargs['table'].lower()))
        context['change_perm'] = self.request.user.has_perm("{0}.change_{1}".format(self.app,self.kwargs['table'].lower()))
        context['delete_perm'] = self.request.user.has_perm("{0}.change_{1}".format(self.app,self.kwargs['table'].lower()))


        if not self.request.user.is_superuser:
            if self.kwargs['table'] == 'Myprotocol':
                protocoltype = self.request.GET.get('protocoltype')
                context['protocoltype'] = protocoltype
                
            context['myname'] = self.model._meta.verbose_name
            context['name'] = self.name
            mycreate = str(self.app) + ':data-create'
            create_button = reverse_lazy(mycreate, kwargs={'is_list':1,'app':self.app,'table':self.kwargs['table']})
            context['create_button'] = create_button
            if self.kwargs['table'] == 'Document':
                args = {}
                args['del_f'] = 0
                args['user_id'] = self.request.user.pk
                

                protocol = Protocol.objects.all()
                archived = self.model.objects.filter(**args).filter(pk__in=[i.document_id for i in protocol]).order_by('-protocol__protocolid')
                context['archived'] = archived

        return context

Mi plantilla de lista de datos (list.html):

{% if data %}    
  <table id="myTable1" class="table table-hover">
      
    <thead id="header">
      <tr class="mycol__">
        <th class="acol1"><a href="#"><i class="fa fa-sync fa-lg"></i> </a></th>
        <th class="col2">Field 1 </th>
        <th class="col3">Field 2</th>         
        <th class="col4">Field 3</th>
        <th class="col5">Field 4</th>
        <th class="col6">Field 5</th>
        <th class="col7">Field 6</th>
        <th class="col9">Field 7</th>
      </tr>
    </thead>
    <tbody id="tbody">
 <!-- HERE IS WHERE THE AJAX SHOULD DISPLAY THE OBJECTS ONE ROW AT A TIME -->
    </tbody>
  </table>
{% endif %}

Mi plantilla de cuerpo de tabla parcial (list_table_body.html):

<tr>        
  <td class="col2">{{ d.protocolid }}</a></td>
  <td class="col4">{{ d.year }}</td>
  <td class="col5">{{ d.pcreated }}</td>
  <td class="col6">{{ d.subject }}</td>
  <td class="col7">{{ d.psender|default_if_none:"< Me >"  }}</td>
</tr>

EDITAR 17-1-2022:

Comencé agregando lo siguiente a list.html:

{% if data %}
  {% for dataitem in data %}
    $.ajax({
        headers: {'X-CSRFToken':getCookie('csrftoken')},
        url: "{% url 'protocol:myprotocol-item' dataitem.protocol_ptr.id %}",//'/protocol/myprotocol/item/'+dataitem.protocol_ptr+'/',
        type: 'get',
        dataType: 'json',
        success: function(data) {
          var tablerows = $('#myTable1 #tbody tr').length;
          // alert(tablerows);
          if (tablerows === 0){
            $("#myTable1 #tbody").append(data.html_form);  
          }
          else if (tablerows > 0){
            $("#myTable1 #tbody tr").last().after(data.html_form);
            
                       
          }
          // let the tablesorter plugin know that we made a update
          // the resort flag set to anything BUT false (no quotes) will trigger an automatic
          // table resort using the current sort
          var resort = true;
          $("table").trigger("update", [resort]); 
        }, // end of success
        error : function(xhr,errmsg,err) {
          console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
        } // end of error   

    }); //end of ajax
  {% endfor %}
{% endif %}

Luego, creé una función de python en views.py que verifica la identificación proporcionada por ajax y devuelve el objeto específico en json, usando list_table_body.html como plantilla.

Funciona, pero estaba pensando: a medida que se cargan las filas, significa que envía una cantidad de llamadas GET, igual a la cantidad total de objetos. ¿Afectará esto al rendimiento del servidor?

Answer

¿Has echado un vistazo a jsGrid ? Viene con algunas campanas y silbatos como paginación y demás, listos para usar. Usé esta solución junto con Django REST Framework para lograr un objetivo similar.