# Pagination HTMX partial

**URL:** https://forum.djangoproject.com/t/pagination-htmx-partial/37643
**Category:** Templates & Frontend
**Created:** [January 2, 2025, 6:44pm UTC](https://forum.djangoproject.com/t/pagination-htmx-partial/37643 "2025-01-02T18:44:31Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![jbhrfx9280](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jbhrfx9280/32/15247_2.png) [@jbhrfx9280](https://forum.djangoproject.com/u/jbhrfx9280)
#### Post date: [January 2, 2025, 6:44pm UTC](https://forum.djangoproject.com/t/pagination-htmx-partial/37643/1 "2025-01-02T18:44:31Z")

</div>

Django 5.1.4  
Python 3.12.4

Hi Guys, Once again I seek advice from you. Basically doing a partial page load (table) using HTMX and have no issues doing that. It’s when I try to do pagination. Either the paging won’t page forward but does show there are 2 pages (page\_obj : \<Page 1 of 2\>), or streams rows off the page indefinitely or the page control UI itself does not show at all. I have tried all I can come up with to get this to work.

Outline:  
I’m loading / setting up an initial page (_base\_console\_ap.html_) then have an **hx-get** to a view( **console\_view** - no data returned) to ‘load’ a partial page ( **task\_index.html** ) this also includes a partial page ( **task\_list.html** ) some table elements `<thead> <tbody>` with hx-get to task\_index view that returns data and loads task\_row.html etc…  
The code below shows the first page of data and the paging UI with a page count of 2 but will not page forward.

**base\_console\_ap.html**

```auto
<div class="container-fluid rounded border" style="max-width:1100px; height:510px">
            <div class="row">
                <div class="col">
                  <div id="task_indx"  
                  hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
                  hx-trigger="load" 
                  hx-get="{% url 'console_app:console_view' %}" 
                  hx-target="this"> 
                </div>
            </div>
</div>

```

views.py(console\_view)

```auto
def console_view(request):
    
    if request.htmx:
        template = 'console_app/task_index.html'
        return render(request, template, {})
    else:
        data = []
        fls_msg_cnt = ManagerMessages.objects.filter().count()
        nodes_cnt = Nodes.objects.filter().count()
        ToLog_cnt = ToLog.objects.filter().count()
        data = {
            'labels': ['Messages', 'Ivanti', 'SolarWinds', 'Control-M', 'TurnOver Log'],
            'values': [fls_msg_cnt, nodes_cnt, 28, 21, ToLog_cnt] 
        }
        template = 'console_app/base_console_ap.html'
        return render(request, template, {'chart_data': data})

```

**views.py** (task\_view)

```auto
def task_index(request):
    if request.htmx:
        print('htmx - task_index')
        tasks = Task.objects.all().order_by(_TASK_ORDBY)   
        paginator = Paginator(tasks, 4)  
        page_number = request.GET.get('page')  
        try:
            page_obj = paginator.page(page_number) 
        except PageNotAnInteger:
            page_obj = paginator.page(1) 
        except EmptyPage:
            page_obj = paginator.page(paginator.num_pages)
        template = 'console_app/task_row.html'
        return render(request, template, {'page_obj': page_obj, 'count': tasks.count()})  

```

**task\_index.html**

```auto
<div id="taskList">
    {% include 'console_app/task_list.html' %}
</div>

```

**task\_list.html**

```auto
<tbody id="taskrows" hx-trigger="load" hx-get="{% url 'console_app:task_index' %}" hx-target="this">
                <tr>
                    <td class="spinner-border" role="status">
                    <span class="visually-hidden">Loading please wait...</span>
                    </td>
                </tr>
</tbody>

```

**task\_row.html**

```auto
{% for task in page_obj %}

<tr>
  {% if task.completed %}
    <td style="word-wrap: break-word;min-width: 160px;max-width: 260px;"><s>{{ task.task }}</s></td>
  {% else %}
    <td style="word-wrap: break-word;min-width: 160px;max-width: 260px;">{{ task.task }}</td>
  {% endif %}
  
  <!-- <td>{{ task.completed }}</td> -->
  <td>{{ task.created_on }}</td> 
  <td>{{ task.notes }}</td> 
  <td>
  {% if task.completed %} 
    <button type="button" class="btn-sm btn btn-outline-secondary" disabled>&checkmark;</button>
  {% else %}
    <button type="button" class="btn-sm btn btn-outline-success" hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}' hx-get="{% url 'console_app:mark_task' task.pk %}" hx-target="#taskList" style="cursor: pointer;">✔</button>
  {% endif %}

    <button type="button" 
     class="btn-sm btn btn-outline-danger" 
     hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
     hx-get="{% url 'console_app:delete_task' task.pk %}" 
     hx-target="#taskList" 
     hx-confirm="Are you sure you wish to delete?" 
     style="cursor: pointer;">
     DEL
    </button>
  
    <button type="button"
     class="btn-sm btn btn-outline-warning" 
     hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
     hx-get="{% url 'console_app:edit_task' task.pk %}" 
     hx-target="closest tr" 
     hx-swap="outerHTML" 
     style="cursor: pointer;">
     Edit
    </button>
  </td>
</tr>
{% endfor %}

<div class="btn-group" role="group" aria-label="Item pagination">
  {% if page_obj.has_previous %}
      <a href="?page={{ page_obj.previous_page_number }}" class="btn btn-outline-primary">&laquo;</a>
  {% endif %}

  {% for page_number in page_obj.paginator.page_range %}
      {% if page_obj.number == page_number %}
          <button class="btn-sm btn btn btn-primary active">
              
              <span>{{ page_number }} <span class="sr-only">(current)</span></span>
          </button>
      {% else %}
          <a href="?page={{ page_number }}" class="btn btn-outline-primary">
              {{ page_number }}
          </a>
      {% endif %}
  {% endfor %}

  {% if page_obj.has_next %}
      <a href="?page={{ page_obj.next_page_number }}" class="btn btn-outline-primary">&raquo;</a>
  {% endif %}
</div>
<div>Total record count: {{ count }}</div>

```

 ![{D0C325B5-0DEA-480C-9EFE-F7924AEC84AD}](https://us1.discourse-cdn.com/flex026/uploads/djangoproject/original/3X/0/3/03fcf6786bec476184d3c2de64c696e8ff0d3fd5.png)

---

<div class="post-metadata">

### Author: ![anefta](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/anefta/32/10649_2.png) [@anefta](https://forum.djangoproject.com/u/anefta)
#### Post date: [January 4, 2025, 3:27pm UTC](https://forum.djangoproject.com/t/pagination-htmx-partial/37643/2 "2025-01-04T15:27:05Z")

</div>

The pagination links need to trigger HTMX requests and update the correct target.

**First** , modify your **task\_index.html** to include a container for the table and pagination:

```auto
<div id="task-container">
    <table class="table">
        <thead>
            <tr>
                <th>Task</th>
                <th>Created On</th>
                <th>Notes</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody id="taskList"
               hx-trigger="load"
               hx-get="{% url 'console_app:task_index' %}"
               hx-target="this">
        </tbody>
    </table>
</div>

```

**Second** , update your **task\_row.html** to include HTMX attributes in the pagination links:

```auto
{% for task in page_obj %}
<tr>
    <td>{{ task.task }}</td>
    <td>{{ task.created_on }}</td> 
    <td>{{ task.notes }}</td> 
    <td>
        <button type="button" hx-get="{% url 'console_app:mark_task' task.pk %}" hx-target="#taskList">✔</button>
        <button type="button" hx-get="{% url 'console_app:delete_task' task.pk %}" hx-target="#taskList">DEL</button>
        <button type="button" hx-get="{% url 'console_app:edit_task' task.pk %}" hx-target="closest tr">Edit</button>
    </td>
</tr>
{% endfor %}

<tr>
    <td colspan="4">
        <div class="btn-group" role="group" aria-label="Item pagination">
            {% if page_obj.has_previous %}
                <button class="btn btn-outline-primary"
                        hx-get="{% url 'console_app:task_index' %}?page={{ page_obj.previous_page_number }}"
                        hx-target="#taskList">&laquo;</button>
            {% endif %}

            {% for page_number in page_obj.paginator.page_range %}
                {% if page_obj.number == page_number %}
                    <button class="btn btn-primary active">{{ page_number }}</button>
                {% else %}
                    <button class="btn btn-outline-primary"
                            hx-get="{% url 'console_app:task_index' %}?page={{ page_number }}"
                            hx-target="#taskList">{{ page_number }}</button>
                {% endif %}
            {% endfor %}

            {% if page_obj.has_next %}
                <button class="btn btn-outline-primary"
                        hx-get="{% url 'console_app:task_index' %}?page={{ page_obj.next_page_number }}"
                        hx-target="#taskList">&raquo;</button>
            {% endif %}
        </div>
        <div>Total record count: {{ count }}</div>
    </td>
</tr>

```

**Third** , update your view to handle both initial and HTMX requests:

```auto
def task_index(request):
    tasks = Task.objects.all().order_by(_TASK_ORDBY)   
    paginator = Paginator(tasks, 4)  
    page_number = request.GET.get('page', 1)  
    
    try:
        page_obj = paginator.page(page_number) 
    except PageNotAnInteger:
        page_obj = paginator.page(1) 
    except EmptyPage:
        page_obj = paginator.page(paginator.num_pages)

    context = {
        'page_obj': page_obj,
        'count': tasks.count()
    }

    if request.htmx:
        template = 'console_app/task_row.html'
    else:
        template = 'console_app/task_index.html'
    
    return render(request, template, context)

```

**Fourth** , I suppose that your **urls.py** contain something like this:

```auto
urlpatterns = [
    path('console/', console_view, name='console_view'),
    path('tasks/', task_index, name='task_index'),

]

```

---

<div class="post-metadata">

### Author: ![jbhrfx9280](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jbhrfx9280/32/15247_2.png) [@jbhrfx9280](https://forum.djangoproject.com/u/jbhrfx9280)
#### Post date: [January 12, 2025, 4:03am UTC](https://forum.djangoproject.com/t/pagination-htmx-partial/37643/3 "2025-01-12T04:03:11Z")

</div>

Thank you so much.  
It really worked great. I believe it also fixes the filter issues with pagination. I will test that and update. Thank you once again.

---

<div class="post-metadata">

### Author: ![anefta](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/anefta/32/10649_2.png) [@anefta](https://forum.djangoproject.com/u/anefta)
#### Post date: [January 12, 2025, 11:15am UTC](https://forum.djangoproject.com/t/pagination-htmx-partial/37643/4 "2025-01-12T11:15:39Z")

</div>

Glad it helped! HTMX is amazing!
