Is there a way to paginate multiple fields in a single view?

I need to implement endless scroll in a complicated way, and I cannot find any information about how to do it.

I have a class-based view that uses a string that represents a room number and displays the offices and computers that are in this room. There are two separate models for the offices and computers (none for rooms), but both have a ‘room’ TextField that can be matched with the room number passed to the view. To display this in the template, I use Bootstrap5 tables with scroll functionality for both the offices and computers.

For very long lists, it can take a long time or fail to load anything at all, so I need to implement a sort of pagination in order to mitigate this issue. Endless scroll seems like the most reasonable option as well. However, I have tried several implementations and all seem to be for Views with only a single model and a single QuerySet or list. Is it possible to paginate multiple QuerySets in a single view?

Here is some of the code I use in my django project:

Views.py:

// A view for displaying all offices (people) and computers within a specific room
class RoomDetail(TemplateView):
    # Display view for rooms
    context_object_name = 'roomNum'
    template_name = 'app/room_detail.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        room = self.kwargs["pk"]

        context['roomNum'] = room
        context['office_list'] = Organizer.getRoomOffices(room) // Returns list[list[str]]

        cpuList = Organizer.getRoomComputers(room) // Return list[list[str]]
        context['computer_list'] = cpuList[0]
        context['computer_message'] = cpuList[1]

        return context

room_detail.html:

<div class="table-responsive table-fixed-header">
  <table class="table">
    <thead>
      <tr>
        <th scope="col">Computer</th>
        <th scope="col">Prime User</th>
        <th scope="col">OS</th>
      </tr>
    </thead>
    <tbody>
      {% for computer, prime_user, os in computer_list %}
      <tr>
        <td><a href="{% url 'app:machine' computer %}" >{{ computer }}</a></td>
        <td><a href="{% url 'app:people' prime_user %}" >{{ prime_user }}</a></td>
        <td>{{ os }}</td>
      </tr>
      {% empty %}
      <tr>
        <td>{{ computer_message }}</td>
        <td></td>
        <td></td>
      </tr>
      {% endfor %}
    </tbody>
  </table>     
</div>
// Similar implementation for the offices in a room

Yes, but it’s going to be up to you to figure out which portions of which querysets get rendered on a particular page. For example, if you’ve got three querysets that each return 17 items, and the page being requested is for items 31-40, then it’s up to you to figure out that you need the last 4 elements of queryset #2 and the first 6 elements of queryset #3 to form that list - or to even calculate that there are 5 pages total for the complete set.

This becomes even more difficult when you have “interleaved” results with some different number of subordinate row for each major row. In those cases, you may need to iterate through the entire set of data to calculate what you need.
Using your example, if you want to generate a list of computers per room, organized by room, where each computer is one “line” of the page, you’ll need to calculate all the rooms that would be displayed before page “X” to be able to create page “X”.

So it can be done - the degree of difficulty depends upon the precise manner in which you want this data organized for display.

As-is, this may be sufficient for most practical requirements. However, it does not scale all that well without possibly adding additional indexes or metadata for that data.

I have the data for computers and offices separated by tabs like so:

<div class="tab-info">
  <ul class="nav nav-tabs" id="myTab" role="tablist">
    <li class="nav-item" role="presentation">
      <button class="nav-link active" id="computers-tab" data-bs-toggle="tab" data-bs-target="#computers" type="button" role="tab" aria-controls="computers" aria-selected="true">Computers</button>
    </li>
    <li class="nav-item" role="presentation">
      <button class="nav-link" id="offices-tab" data-bs-toggle="tab" data-bs-target="#offices" type="button" role="tab" aria-controls="offices" aria-selected="false">Offices</button>
    </li>
  </ul>
  <div class="tab-content" id="myTabContent">
    <div class="tab-pane fade show active" id="computers" role="tabpanel" aria-labelledby="computers-tab">
      <div class="table-responsive table-fixed-header">
        <table class="table">

        // Table displaying rows of computers, omitted for readability

        </table>     
      </div>
    </div>
    <div class="tab-pane fade" id="offices" role="tabpanel" aria-labelledby="offices-tab">
      <div class="table-responsive table-fixed-header">
        <table class="table">

          // Table for displaying rows of offices, omitted for readability

        </table>
      </div>
    </div>
  </div>

The two QuerySets are separate with no interleaving. Would it also be possible (or reasonable) to create 2 paginated helper view functions for each QuerySet that is called by the parent Room view?

Are you saying that you’re looking to have two separate and independent parts of your page (“tabs”), where each are rendered and paginated separately?

You can do this, but you’re going to want to do this using AJAX and partial page refreshes, because if you do a whole-page refresh, you’re going to need to track both tab status with every page request.

(If I needed to do this, I’d probably create separate views to produce the partial-page updates for each tab.)

That’s exactly what I am trying to do yes. Would the separate views for each tab use AJAX too then?

Yes. If you’re using something like an infinite scroll for these, there’s no need to do anything with “tab A” if you’re looking at (and scrolling) “tab B”.

Sounds good, I’ll start working on it using this approach. Thank you so much for the help!