Просмотр исходного кода

#8057: Enable dynamic tables for object list views

jeremystretch 4 лет назад
Родитель
Сommit
91f33d3289

+ 7 - 0
netbox/netbox/views/generic.py

@@ -23,6 +23,7 @@ from utilities.exceptions import AbortTransaction, PermissionsViolation
 from utilities.forms import (
 from utilities.forms import (
     BootstrapMixin, BulkRenameForm, ConfirmationForm, CSVDataField, CSVFileField, ImportForm, restrict_form_fields,
     BootstrapMixin, BulkRenameForm, ConfirmationForm, CSVDataField, CSVFileField, ImportForm, restrict_form_fields,
 )
 )
+from utilities.htmx import is_htmx
 from utilities.permissions import get_permission_for_model
 from utilities.permissions import get_permission_for_model
 from utilities.tables import paginate_table
 from utilities.tables import paginate_table
 from utilities.utils import normalize_querydict, prepare_cloned_fields
 from utilities.utils import normalize_querydict, prepare_cloned_fields
@@ -185,6 +186,12 @@ class ObjectListView(ObjectPermissionRequiredMixin, View):
         table = self.get_table(request, permissions)
         table = self.get_table(request, permissions)
         paginate_table(table, request)
         paginate_table(table, request)
 
 
+        # If this is an HTMX request, return only the rendered table HTML
+        if is_htmx(request):
+            return render(request, 'htmx/table.html', {
+                'table': table,
+            })
+
         context = {
         context = {
             'content_type': content_type,
             'content_type': content_type,
             'table': table,
             'table': table,

+ 3 - 0
netbox/templates/base/base.html

@@ -160,6 +160,9 @@
       })();
       })();
     </script>
     </script>
 
 
+    {# TODO: Package HTMX JS locally #}
+    <script src="https://unpkg.com/htmx.org@1.6.1"></script>
+
     {# Page layout #}
     {# Page layout #}
     {% block layout %}{% endblock %}
     {% block layout %}{% endblock %}
 
 

+ 3 - 4
netbox/templates/generic/object_list.html

@@ -95,10 +95,11 @@
 
 
         {# Object table #}
         {# Object table #}
         <div class="card">
         <div class="card">
-          <div class="card-body">
+          <div class="card-body" id="object_list">
             <div class="table-responsive">
             <div class="table-responsive">
-              {% render_table table 'inc/table.html' %}
+              {% render_table table 'inc/table_htmx.html' %}
             </div>
             </div>
+            {% include 'inc/paginator_htmx.html' with paginator=table.paginator page=table.page %}
           </div>
           </div>
         </div>
         </div>
 
 
@@ -125,8 +126,6 @@
 
 
       </form>
       </form>
 
 
-      {# Paginator #}
-      {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
     </div>
     </div>
 
 
     {# Filter form #}
     {# Filter form #}

+ 5 - 0
netbox/templates/htmx/table.html

@@ -0,0 +1,5 @@
+{# Render an HTML table element #}
+{% load render_table from django_tables2 %}
+
+{% render_table table 'inc/table_htmx.html' %}
+{% include 'inc/paginator_htmx.html' with paginator=table.paginator page=table.page %}

+ 43 - 0
netbox/templates/inc/paginator_htmx.html

@@ -0,0 +1,43 @@
+{% load helpers %}
+
+<div class="row">
+  <div class="col col-md-6 mb-0">
+    {% if paginator.num_pages > 1 %}
+      <div class="btn-group btn-group-sm" role="group" aria-label="Pages">
+        {% if page.has_previous %}
+          <a href="#" hx-get="{% querystring request page=page.previous_page_number %}" hx-target="#object_list" class="btn btn-outline-secondary">
+            <i class="mdi mdi-chevron-double-left"></i>
+          </a>
+        {% endif %}
+        {% for p in page.smart_pages %}
+          {% if p %}
+            <a href="#" hx-get="{% querystring request page=p %}" hx-target="#object_list" class="btn btn-outline-secondary{% if page.number == p %} active{% endif %}">
+              {{ p }}
+            </a>
+          {% else %}
+            <button type="button" class="btn btn-outline-secondary" disabled>
+              <span>&hellip;</span>
+            </button>
+          {% endif %}
+        {% endfor %}
+        {% if page.has_next %}
+          <a href="#" hx-get="{% querystring request page=page.next_page_number %}" hx-target="#object_list" class="btn btn-outline-secondary">
+            <i class="mdi mdi-chevron-double-right"></i>
+          </a>
+        {% endif %}
+      </div>
+    {% endif %}
+  </div>
+  <div class="col col-md-6 mb-0 text-end">
+    <div class="dropdown dropup">
+      <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">
+        Per Page
+      </button>
+      <ul class="dropdown-menu">
+        {% for n in page.paginator.get_page_lengths %}
+          <li><a class="dropdown-item" href="#" hx-get="{% querystring request per_page=n %}" hx-target="#object_list">{{ n }}</a></li>
+        {% endfor %}
+      </ul>
+    </div>
+  </div>
+</div>

+ 41 - 0
netbox/templates/inc/table_htmx.html

@@ -0,0 +1,41 @@
+{% load django_tables2 %}
+
+<table{% if table.attrs %} {{ table.attrs.as_html }}{% endif %}>
+  {% if table.show_header %}
+    <thead>
+      <tr>
+        {% for column in table.columns %}
+          {% if column.orderable %}
+            <th {{ column.attrs.th.as_html }}><a href="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}" hx-get="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}" hx-target="#object_list">{{ column.header }}</a></th>
+          {% else %}
+            <th {{ column.attrs.th.as_html }}>{{ column.header }}</th>
+          {% endif %}
+        {% endfor %}
+      </tr>
+    </thead>
+  {% endif %}
+  <tbody>
+    {% for row in table.page.object_list|default:table.rows %}
+      <tr {{ row.attrs.as_html }}>
+        {% for column, cell in row.items %}
+          <td {{ column.attrs.td.as_html }}>{{ cell }}</td>
+        {% endfor %}
+      </tr>
+    {% empty %}
+      {% if table.empty_text %}
+        <tr>
+          <td colspan="{{ table.columns|length }}" class="text-center text-muted">&mdash; {{ table.empty_text }} &mdash;</td>
+        </tr>
+      {% endif %}
+    {% endfor %}
+  </tbody>
+  {% if table.has_footer %}
+    <tfoot>
+      <tr>
+        {% for column in table.columns %}
+          <td>{{ column.footer }}</td>
+        {% endfor %}
+      </tr>
+    </tfoot>
+  {% endif %}
+</table>

+ 5 - 0
netbox/utilities/htmx.py

@@ -0,0 +1,5 @@
+def is_htmx(request):
+    """
+    Returns True if the request was made by HTMX; False otherwise.
+    """
+    return 'Hx-Request' in request.headers