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

Enable HTMX for all ObjectChildrenViews

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

+ 17 - 0
netbox/dcim/views.py

@@ -797,41 +797,49 @@ class DeviceTypeView(generic.ObjectView):
 class DeviceTypeConsolePortsView(DeviceTypeComponentsView):
 class DeviceTypeConsolePortsView(DeviceTypeComponentsView):
     child_model = ConsolePortTemplate
     child_model = ConsolePortTemplate
     table = tables.ConsolePortTemplateTable
     table = tables.ConsolePortTemplateTable
+    filterset = filtersets.ConsolePortTemplateFilterSet
 
 
 
 
 class DeviceTypeConsoleServerPortsView(DeviceTypeComponentsView):
 class DeviceTypeConsoleServerPortsView(DeviceTypeComponentsView):
     child_model = ConsoleServerPortTemplate
     child_model = ConsoleServerPortTemplate
     table = tables.ConsoleServerPortTemplateTable
     table = tables.ConsoleServerPortTemplateTable
+    filterset = filtersets.ConsoleServerPortTemplateFilterSet
 
 
 
 
 class DeviceTypePowerPortsView(DeviceTypeComponentsView):
 class DeviceTypePowerPortsView(DeviceTypeComponentsView):
     child_model = PowerPortTemplate
     child_model = PowerPortTemplate
     table = tables.PowerPortTemplateTable
     table = tables.PowerPortTemplateTable
+    filterset = filtersets.PowerPortTemplateFilterSet
 
 
 
 
 class DeviceTypePowerOutletsView(DeviceTypeComponentsView):
 class DeviceTypePowerOutletsView(DeviceTypeComponentsView):
     child_model = PowerOutletTemplate
     child_model = PowerOutletTemplate
     table = tables.PowerOutletTemplateTable
     table = tables.PowerOutletTemplateTable
+    filterset = filtersets.PowerOutletTemplateFilterSet
 
 
 
 
 class DeviceTypeInterfacesView(DeviceTypeComponentsView):
 class DeviceTypeInterfacesView(DeviceTypeComponentsView):
     child_model = InterfaceTemplate
     child_model = InterfaceTemplate
     table = tables.InterfaceTemplateTable
     table = tables.InterfaceTemplateTable
+    filterset = filtersets.InterfaceTemplateFilterSet
 
 
 
 
 class DeviceTypeFrontPortsView(DeviceTypeComponentsView):
 class DeviceTypeFrontPortsView(DeviceTypeComponentsView):
     child_model = FrontPortTemplate
     child_model = FrontPortTemplate
     table = tables.FrontPortTemplateTable
     table = tables.FrontPortTemplateTable
+    filterset = filtersets.FrontPortTemplateFilterSet
 
 
 
 
 class DeviceTypeRearPortsView(DeviceTypeComponentsView):
 class DeviceTypeRearPortsView(DeviceTypeComponentsView):
     child_model = RearPortTemplate
     child_model = RearPortTemplate
     table = tables.RearPortTemplateTable
     table = tables.RearPortTemplateTable
+    filterset = filtersets.RearPortTemplateFilterSet
 
 
 
 
 class DeviceTypeDeviceBaysView(DeviceTypeComponentsView):
 class DeviceTypeDeviceBaysView(DeviceTypeComponentsView):
     child_model = DeviceBayTemplate
     child_model = DeviceBayTemplate
     table = tables.DeviceBayTemplateTable
     table = tables.DeviceBayTemplateTable
+    filterset = filtersets.DeviceBayTemplateFilterSet
 
 
 
 
 class DeviceTypeEditView(generic.ObjectEditView):
 class DeviceTypeEditView(generic.ObjectEditView):
@@ -1328,30 +1336,35 @@ class DeviceView(generic.ObjectView):
 class DeviceConsolePortsView(DeviceComponentsView):
 class DeviceConsolePortsView(DeviceComponentsView):
     child_model = ConsolePort
     child_model = ConsolePort
     table = tables.DeviceConsolePortTable
     table = tables.DeviceConsolePortTable
+    filterset = filtersets.ConsolePortFilterSet
     template_name = 'dcim/device/consoleports.html'
     template_name = 'dcim/device/consoleports.html'
 
 
 
 
 class DeviceConsoleServerPortsView(DeviceComponentsView):
 class DeviceConsoleServerPortsView(DeviceComponentsView):
     child_model = ConsoleServerPort
     child_model = ConsoleServerPort
     table = tables.DeviceConsoleServerPortTable
     table = tables.DeviceConsoleServerPortTable
+    filterset = filtersets.ConsoleServerPortFilterSet
     template_name = 'dcim/device/consoleserverports.html'
     template_name = 'dcim/device/consoleserverports.html'
 
 
 
 
 class DevicePowerPortsView(DeviceComponentsView):
 class DevicePowerPortsView(DeviceComponentsView):
     child_model = PowerPort
     child_model = PowerPort
     table = tables.DevicePowerPortTable
     table = tables.DevicePowerPortTable
+    filterset = filtersets.PowerPortFilterSet
     template_name = 'dcim/device/powerports.html'
     template_name = 'dcim/device/powerports.html'
 
 
 
 
 class DevicePowerOutletsView(DeviceComponentsView):
 class DevicePowerOutletsView(DeviceComponentsView):
     child_model = PowerOutlet
     child_model = PowerOutlet
     table = tables.DevicePowerOutletTable
     table = tables.DevicePowerOutletTable
+    filterset = filtersets.PowerOutletFilterSet
     template_name = 'dcim/device/poweroutlets.html'
     template_name = 'dcim/device/poweroutlets.html'
 
 
 
 
 class DeviceInterfacesView(DeviceComponentsView):
 class DeviceInterfacesView(DeviceComponentsView):
     child_model = Interface
     child_model = Interface
     table = tables.DeviceInterfaceTable
     table = tables.DeviceInterfaceTable
+    filterset = filtersets.InterfaceFilterSet
     template_name = 'dcim/device/interfaces.html'
     template_name = 'dcim/device/interfaces.html'
 
 
     def get_children(self, request, parent):
     def get_children(self, request, parent):
@@ -1364,24 +1377,28 @@ class DeviceInterfacesView(DeviceComponentsView):
 class DeviceFrontPortsView(DeviceComponentsView):
 class DeviceFrontPortsView(DeviceComponentsView):
     child_model = FrontPort
     child_model = FrontPort
     table = tables.DeviceFrontPortTable
     table = tables.DeviceFrontPortTable
+    filterset = filtersets.FrontPortFilterSet
     template_name = 'dcim/device/frontports.html'
     template_name = 'dcim/device/frontports.html'
 
 
 
 
 class DeviceRearPortsView(DeviceComponentsView):
 class DeviceRearPortsView(DeviceComponentsView):
     child_model = RearPort
     child_model = RearPort
     table = tables.DeviceRearPortTable
     table = tables.DeviceRearPortTable
+    filterset = filtersets.RearPortFilterSet
     template_name = 'dcim/device/rearports.html'
     template_name = 'dcim/device/rearports.html'
 
 
 
 
 class DeviceDeviceBaysView(DeviceComponentsView):
 class DeviceDeviceBaysView(DeviceComponentsView):
     child_model = DeviceBay
     child_model = DeviceBay
     table = tables.DeviceDeviceBayTable
     table = tables.DeviceDeviceBayTable
+    filterset = filtersets.DeviceBayFilterSet
     template_name = 'dcim/device/devicebays.html'
     template_name = 'dcim/device/devicebays.html'
 
 
 
 
 class DeviceInventoryView(DeviceComponentsView):
 class DeviceInventoryView(DeviceComponentsView):
     child_model = InventoryItem
     child_model = InventoryItem
     table = tables.DeviceInventoryItemTable
     table = tables.DeviceInventoryItemTable
+    filterset = filtersets.InventoryItemFilterSet
     template_name = 'dcim/device/inventory.html'
     template_name = 'dcim/device/inventory.html'
 
 
 
 

+ 9 - 2
netbox/ipam/views.py

@@ -1,21 +1,22 @@
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
 from django.db.models import Prefetch
 from django.db.models import Prefetch
 from django.db.models.expressions import RawSQL
 from django.db.models.expressions import RawSQL
-from django.http import Http404
 from django.shortcuts import get_object_or_404, redirect, render
 from django.shortcuts import get_object_or_404, redirect, render
 from django.urls import reverse
 from django.urls import reverse
 
 
+from dcim.filtersets import InterfaceFilterSet
 from dcim.models import Device, Interface, Site
 from dcim.models import Device, Interface, Site
 from dcim.tables import SiteTable
 from dcim.tables import SiteTable
 from netbox.views import generic
 from netbox.views import generic
 from utilities.tables import paginate_table
 from utilities.tables import paginate_table
 from utilities.utils import count_related
 from utilities.utils import count_related
+from virtualization.filtersets import VMInterfaceFilterSet
 from virtualization.models import VirtualMachine, VMInterface
 from virtualization.models import VirtualMachine, VMInterface
 from . import filtersets, forms, tables
 from . import filtersets, forms, tables
 from .constants import *
 from .constants import *
 from .models import *
 from .models import *
 from .models import ASN
 from .models import ASN
-from .utils import add_available_ipaddresses, add_requested_prefixes, add_available_vlans
+from .utils import add_requested_prefixes, add_available_vlans
 
 
 
 
 #
 #
@@ -457,6 +458,7 @@ class PrefixPrefixesView(generic.ObjectChildrenView):
     queryset = Prefix.objects.all()
     queryset = Prefix.objects.all()
     child_model = Prefix
     child_model = Prefix
     table = tables.PrefixTable
     table = tables.PrefixTable
+    filterset = filtersets.PrefixFilterSet
     template_name = 'ipam/prefix/prefixes.html'
     template_name = 'ipam/prefix/prefixes.html'
 
 
     def get_children(self, request, parent):
     def get_children(self, request, parent):
@@ -483,6 +485,7 @@ class PrefixIPRangesView(generic.ObjectChildrenView):
     queryset = Prefix.objects.all()
     queryset = Prefix.objects.all()
     child_model = IPRange
     child_model = IPRange
     table = tables.IPRangeTable
     table = tables.IPRangeTable
+    filterset = filtersets.IPRangeFilterSet
     template_name = 'ipam/prefix/ip_ranges.html'
     template_name = 'ipam/prefix/ip_ranges.html'
 
 
     def get_children(self, request, parent):
     def get_children(self, request, parent):
@@ -499,6 +502,7 @@ class PrefixIPAddressesView(generic.ObjectChildrenView):
     queryset = Prefix.objects.all()
     queryset = Prefix.objects.all()
     child_model = IPAddress
     child_model = IPAddress
     table = tables.IPAddressTable
     table = tables.IPAddressTable
+    filterset = filtersets.IPAddressFilterSet
     template_name = 'ipam/prefix/ip_addresses.html'
     template_name = 'ipam/prefix/ip_addresses.html'
 
 
     def get_children(self, request, parent):
     def get_children(self, request, parent):
@@ -560,6 +564,7 @@ class IPRangeIPAddressesView(generic.ObjectChildrenView):
     queryset = IPRange.objects.all()
     queryset = IPRange.objects.all()
     child_model = IPAddress
     child_model = IPAddress
     table = tables.IPAddressTable
     table = tables.IPAddressTable
+    filterset = filtersets.IPAddressFilterSet
     template_name = 'ipam/iprange/ip_addresses.html'
     template_name = 'ipam/iprange/ip_addresses.html'
 
 
     def get_children(self, request, parent):
     def get_children(self, request, parent):
@@ -959,6 +964,7 @@ class VLANInterfacesView(generic.ObjectChildrenView):
     queryset = VLAN.objects.all()
     queryset = VLAN.objects.all()
     child_model = Interface
     child_model = Interface
     table = tables.VLANDevicesTable
     table = tables.VLANDevicesTable
+    filterset = InterfaceFilterSet
     template_name = 'ipam/vlan/interfaces.html'
     template_name = 'ipam/vlan/interfaces.html'
 
 
     def get_children(self, request, parent):
     def get_children(self, request, parent):
@@ -974,6 +980,7 @@ class VLANVMInterfacesView(generic.ObjectChildrenView):
     queryset = VLAN.objects.all()
     queryset = VLAN.objects.all()
     child_model = VMInterface
     child_model = VMInterface
     table = tables.VLANVirtualMachinesTable
     table = tables.VLANVirtualMachinesTable
+    filterset = VMInterfaceFilterSet
     template_name = 'ipam/vlan/vminterfaces.html'
     template_name = 'ipam/vlan/vminterfaces.html'
 
 
     def get_children(self, request, parent):
     def get_children(self, request, parent):

+ 12 - 1
netbox/netbox/views/generic.py

@@ -84,11 +84,12 @@ class ObjectChildrenView(ObjectView):
     queryset = None
     queryset = None
     child_model = None
     child_model = None
     table = None
     table = None
+    filterset = None
     template_name = None
     template_name = None
 
 
     def get_children(self, request, parent):
     def get_children(self, request, parent):
         """
         """
-        Return a QuerySet or iterable of child objects.
+        Return a QuerySet of child objects.
 
 
         request: The current request
         request: The current request
         parent: The parent object
         parent: The parent object
@@ -102,6 +103,9 @@ class ObjectChildrenView(ObjectView):
         instance = get_object_or_404(self.queryset, **kwargs)
         instance = get_object_or_404(self.queryset, **kwargs)
         child_objects = self.get_children(request, instance)
         child_objects = self.get_children(request, instance)
 
 
+        if self.filterset:
+            child_objects = self.filterset(request.GET, child_objects).qs
+
         permissions = {}
         permissions = {}
         for action in ('change', 'delete'):
         for action in ('change', 'delete'):
             perm_name = get_permission_for_model(self.child_model, action)
             perm_name = get_permission_for_model(self.child_model, action)
@@ -113,6 +117,13 @@ class ObjectChildrenView(ObjectView):
             table.columns.show('pk')
             table.columns.show('pk')
         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', {
+                'object': instance,
+                'table': table,
+            })
+
         return render(request, self.get_template_name(), {
         return render(request, self.get_template_name(), {
             'object': instance,
             'object': instance,
             'table': table,
             'table': table,

+ 7 - 4
netbox/templates/dcim/device/consoleports.html

@@ -6,10 +6,14 @@
 {% block content %}
 {% block content %}
   <form method="post">
   <form method="post">
     {% csrf_token %}
     {% csrf_token %}
-    {% include 'inc/table_controls.html' with table_modal="DeviceConsolePortTable_config" %}
-    <div class="table-responsive">
-      {% render_table table 'inc/table.html' %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="DeviceConsolePortTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
     </div>
     </div>
+
     <div class="noprint bulk-buttons">
     <div class="noprint bulk-buttons">
         <div class="bulk-button-group">
         <div class="bulk-button-group">
             {% if perms.dcim.change_consoleport %}
             {% if perms.dcim.change_consoleport %}
@@ -38,6 +42,5 @@
         {% endif %}
         {% endif %}
     </div>
     </div>
   </form>
   </form>
-  {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
   {% table_config_form table %}
   {% table_config_form table %}
 {% endblock %}
 {% endblock %}

+ 7 - 3
netbox/templates/dcim/device/consoleserverports.html

@@ -6,10 +6,14 @@
 {% block content %}
 {% block content %}
   <form method="post">
   <form method="post">
     {% csrf_token %}
     {% csrf_token %}
-    {% include 'inc/table_controls.html' with table_modal="DeviceConsoleServerPortTable_config" %}
-    <div class="table-responsive">
-      {% render_table table 'inc/table.html' %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="DeviceConsoleServerPortTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
     </div>
     </div>
+
     <div class="noprint bulk-buttons">
     <div class="noprint bulk-buttons">
         <div class="bulk-button-group">
         <div class="bulk-button-group">
             {% if perms.dcim.change_consoleserverport %}
             {% if perms.dcim.change_consoleserverport %}

+ 7 - 3
netbox/templates/dcim/device/devicebays.html

@@ -6,10 +6,14 @@
 {% block content %}
 {% block content %}
   <form method="post">
   <form method="post">
     {% csrf_token %}
     {% csrf_token %}
-    {% include 'inc/table_controls.html' with table_modal="DeviceDeviceBayTable_config" %}
-    <div class="table-responsive">
-      {% render_table table 'inc/table.html' %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="DeviceDeviceBayTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
     </div>
     </div>
+
     <div class="noprint bulk-buttons">
     <div class="noprint bulk-buttons">
         <div class="bulk-button-group">
         <div class="bulk-button-group">
             {% if perms.dcim.change_devicebay %}
             {% if perms.dcim.change_devicebay %}

+ 7 - 3
netbox/templates/dcim/device/frontports.html

@@ -6,10 +6,14 @@
 {% block content %}
 {% block content %}
   <form method="post">
   <form method="post">
     {% csrf_token %}
     {% csrf_token %}
-    {% include 'inc/table_controls.html' with table_modal="DeviceFrontPortTable_config" %}
-    <div class="table-responsive">
-      {% render_table table 'inc/table.html' %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="DeviceFrontPortTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
     </div>
     </div>
+
     <div class="noprint bulk-buttons">
     <div class="noprint bulk-buttons">
         <div class="bulk-button-group">
         <div class="bulk-button-group">
             {% if perms.dcim.change_frontport %}
             {% if perms.dcim.change_frontport %}

+ 15 - 3
netbox/templates/dcim/device/interfaces.html

@@ -9,7 +9,15 @@
     <div class="row mb-3 justify-content-between">
     <div class="row mb-3 justify-content-between">
       <div class="col col-12 col-lg-4 my-3 my-lg-0 d-flex noprint table-controls">
       <div class="col col-12 col-lg-4 my-3 my-lg-0 d-flex noprint table-controls">
         <div class="input-group input-group-sm">
         <div class="input-group input-group-sm">
-          <input type="text" class="form-control interface-filter" placeholder="Filter" title="Filter text (regular expressions supported)" />
+          <input
+              type="text"
+              name="q"
+              class="form-control"
+              placeholder="Quick search"
+              hx-get="{{ request.full_path }}"
+              hx-target="#object_list"
+              hx-trigger="keyup changed delay:500ms"
+          />
         </div>
         </div>
       </div>
       </div>
       <div class="col col-md-3 mb-0 d-flex noprint table-controls">
       <div class="col col-md-3 mb-0 d-flex noprint table-controls">
@@ -34,9 +42,13 @@
         </div>
         </div>
       </div>
       </div>
     </div>
     </div>
-    <div class="table-responsive">
-      {% render_table table 'inc/table.html' %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
     </div>
     </div>
+
     <div class="noprint bulk-buttons">
     <div class="noprint bulk-buttons">
         <div class="bulk-button-group">
         <div class="bulk-button-group">
         {% if perms.dcim.change_interface %}
         {% if perms.dcim.change_interface %}

+ 7 - 3
netbox/templates/dcim/device/inventory.html

@@ -6,10 +6,14 @@
 {% block content %}
 {% block content %}
   <form method="post">
   <form method="post">
     {% csrf_token %}
     {% csrf_token %}
-    {% include 'inc/table_controls.html' with table_modal="DeviceInventoryItemTable_config" %}
-    <div class="table-responsive">
-      {% render_table table 'inc/table.html' %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="DeviceInventoryItemTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
     </div>
     </div>
+
     <div class="noprint bulk-buttons">
     <div class="noprint bulk-buttons">
         <div class="bulk-button-group">
         <div class="bulk-button-group">
             {% if perms.dcim.change_inventoryitem %}
             {% if perms.dcim.change_inventoryitem %}

+ 7 - 3
netbox/templates/dcim/device/poweroutlets.html

@@ -6,10 +6,14 @@
 {% block content %}
 {% block content %}
   <form method="post">
   <form method="post">
     {% csrf_token %}
     {% csrf_token %}
-    {% include 'inc/table_controls.html' with table_modal="DevicePowerOutletTable_config" %}
-    <div class="table-responsive">
-      {% render_table table 'inc/table.html' %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="DevicePowerOutletTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
     </div>
     </div>
+
     <div class="noprint bulk-buttons">
     <div class="noprint bulk-buttons">
         <div class="bulk-button-group">
         <div class="bulk-button-group">
             {% if perms.dcim.change_powerport %}
             {% if perms.dcim.change_powerport %}

+ 7 - 3
netbox/templates/dcim/device/powerports.html

@@ -6,10 +6,14 @@
 {% block content %}
 {% block content %}
   <form method="post">
   <form method="post">
     {% csrf_token %}
     {% csrf_token %}
-    {% include 'inc/table_controls.html' with table_modal="DevicePowerPortTable_config" %}
-    <div class="table-responsive">
-      {% render_table table 'inc/table.html' %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="DevicePowerPortTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
     </div>
     </div>
+
     <div class="noprint bulk-buttons">
     <div class="noprint bulk-buttons">
         <div class="bulk-button-group">
         <div class="bulk-button-group">
             {% if perms.dcim.change_powerport %}
             {% if perms.dcim.change_powerport %}

+ 7 - 3
netbox/templates/dcim/device/rearports.html

@@ -6,10 +6,14 @@
 {% block content %}
 {% block content %}
   <form method="post">
   <form method="post">
     {% csrf_token %}
     {% csrf_token %}
-    {% include 'inc/table_controls.html' with table_modal="DeviceRearPortTable_config" %}
-    <div class="table-responsive">
-      {% render_table table 'inc/table.html' %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="DeviceRearPortTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
     </div>
     </div>
+
     <div class="noprint bulk-buttons">
     <div class="noprint bulk-buttons">
         <div class="bulk-button-group">
         <div class="bulk-button-group">
             {% if perms.dcim.change_rearport %}
             {% if perms.dcim.change_rearport %}

+ 7 - 11
netbox/templates/dcim/devicetype/component_templates.html

@@ -7,11 +7,9 @@
     <form method="post">
     <form method="post">
         {% csrf_token %}
         {% csrf_token %}
         <div class="card">
         <div class="card">
-            <h5 class="card-header">
-                {{ title }}
-            </h5>
-            <div class="card-body table-responsive">
-                {% render_table table 'inc/table.html' %}
+            <h5 class="card-header">{{ title }}</h5>
+            <div class="card-body" id="object_list">
+              {% include 'htmx/table.html' %}
             </div>
             </div>
             <div class="card-footer noprint">
             <div class="card-footer noprint">
                 {% if table.rows %}
                 {% if table.rows %}
@@ -37,12 +35,10 @@
     </form>
     </form>
   {% else %}
   {% else %}
     <div class="card">
     <div class="card">
-        <h5 class="card-header">
-            {{ title }}
-        </h5>
-        <div class="card-body table-responsive">
-            {% render_table table 'inc/table.html' %}
-        </div>
+      <h5 class="card-header">{{ title }}</h5>
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
     </div>
     </div>
   {% endif %}
   {% endif %}
 {% endblock content %}
 {% endblock content %}

+ 26 - 4
netbox/templates/ipam/iprange/ip_addresses.html

@@ -1,4 +1,5 @@
 {% extends 'ipam/iprange/base.html' %}
 {% extends 'ipam/iprange/base.html' %}
+{% load helpers %}
 
 
 {% block extra_controls %}
 {% block extra_controls %}
   {% if perms.ipam.add_ipaddress and active_tab == 'ip-addresses' and object.first_available_ip %}
   {% if perms.ipam.add_ipaddress and active_tab == 'ip-addresses' and object.first_available_ip %}
@@ -9,9 +10,30 @@
 {% endblock %}
 {% endblock %}
 
 
 {% block content %}
 {% block content %}
-  <div class="row">
-    <div class="col col-md-12">
-      {% include 'utilities/obj_table.html' with heading='IP Addresses' bulk_edit_url='ipam:ipaddress_bulk_edit' bulk_delete_url='ipam:ipaddress_bulk_delete' %}
+  <form method="post">
+    {% csrf_token %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="IPAddressTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
+    </div>
+
+    <div class="noprint bulk-buttons">
+      <div class="bulk-button-group">
+        {% if perms.ipam.change_ipaddress %}
+          <button type="submit" name="_edit" formaction="{% url 'ipam:ipaddress_bulk_edit' %}?return_url={% url 'ipam:iprange_ipaddresses' pk=object.pk %}" class="btn btn-warning btn-sm">
+            <i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
+          </button>
+        {% endif %}
+        {% if perms.ipam.delete_ipaddress %}
+          <button type="submit" name="_delete" formaction="{% url 'ipam:ipaddress_bulk_delete' %}?return_url={% url 'ipam:iprange_ipaddresses' pk=object.pk %}" class="btn btn-danger btn-sm">
+            <i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
+          </button>
+        {% endif %}
+      </div>
     </div>
     </div>
-  </div>
+  </form>
+  {% table_config_form table %}
 {% endblock %}
 {% endblock %}

+ 25 - 7
netbox/templates/ipam/prefix/ip_addresses.html

@@ -1,6 +1,5 @@
 {% extends 'ipam/prefix/base.html' %}
 {% extends 'ipam/prefix/base.html' %}
 {% load helpers %}
 {% load helpers %}
-{% load static %}
 
 
 {% block extra_controls %}
 {% block extra_controls %}
   {% if perms.ipam.add_ipaddress and first_available_ip %}
   {% if perms.ipam.add_ipaddress and first_available_ip %}
@@ -11,11 +10,30 @@
 {% endblock %}
 {% endblock %}
 
 
 {% block content %}
 {% block content %}
-  <div class="row">
-    <div class="col col-md-12">
-      {% include 'inc/table_controls.html' with table_modal="IPAddressTable_config" %}
-      {% include 'utilities/obj_table.html' with heading='IP Addresses' bulk_edit_url='ipam:ipaddress_bulk_edit' bulk_delete_url='ipam:ipaddress_bulk_delete' %}
+  <form method="post">
+    {% csrf_token %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="IPAddressTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
+    </div>
+
+    <div class="noprint bulk-buttons">
+      <div class="bulk-button-group">
+        {% if perms.ipam.change_ipaddress %}
+          <button type="submit" name="_edit" formaction="{% url 'ipam:ipaddress_bulk_edit' %}?return_url={% url 'ipam:prefix_ipaddresses' pk=object.pk %}" class="btn btn-warning btn-sm">
+            <i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
+          </button>
+        {% endif %}
+        {% if perms.ipam.delete_ipaddress %}
+          <button type="submit" name="_delete" formaction="{% url 'ipam:ipaddress_bulk_delete' %}?return_url={% url 'ipam:prefix_ipaddresses' pk=object.pk %}" class="btn btn-danger btn-sm">
+            <i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
+          </button>
+        {% endif %}
+      </div>
     </div>
     </div>
-  </div>
-  {% table_config_form table table_name="IPAddressTable" %}
+  </form>
+  {% table_config_form table %}
 {% endblock %}
 {% endblock %}

+ 25 - 7
netbox/templates/ipam/prefix/ip_ranges.html

@@ -1,13 +1,31 @@
 {% extends 'ipam/prefix/base.html' %}
 {% extends 'ipam/prefix/base.html' %}
 {% load helpers %}
 {% load helpers %}
-{% load static %}
 
 
 {% block content %}
 {% block content %}
-  <div class="row">
-    <div class="col col-md-12">
-      {% include 'inc/table_controls.html' with table_modal="IPRangeTable_config" %}
-      {% include 'utilities/obj_table.html' with heading='Child IP Ranges' bulk_edit_url='ipam:iprange_bulk_edit' bulk_delete_url='ipam:iprange_bulk_delete' parent=prefix %}
+  <form method="post">
+    {% csrf_token %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="IPRangeTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
+    </div>
+
+    <div class="noprint bulk-buttons">
+      <div class="bulk-button-group">
+        {% if perms.ipam.change_iprange %}
+          <button type="submit" name="_edit" formaction="{% url 'ipam:iprange_bulk_edit' %}?return_url={% url 'ipam:prefix_ipranges' pk=object.pk %}" class="btn btn-warning btn-sm">
+            <i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
+          </button>
+        {% endif %}
+        {% if perms.ipam.delete_iprange %}
+          <button type="submit" name="_delete" formaction="{% url 'ipam:iprange_bulk_delete' %}?return_url={% url 'ipam:prefix_ipranges' pk=object.pk %}" class="btn btn-danger btn-sm">
+            <i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
+          </button>
+        {% endif %}
+      </div>
     </div>
     </div>
-  </div>
-  {% table_config_form table table_name="IPRangeTable" %}
+  </form>
+  {% table_config_form table %}
 {% endblock %}
 {% endblock %}

+ 25 - 7
netbox/templates/ipam/prefix/prefixes.html

@@ -1,6 +1,5 @@
 {% extends 'ipam/prefix/base.html' %}
 {% extends 'ipam/prefix/base.html' %}
 {% load helpers %}
 {% load helpers %}
-{% load static %}
 
 
 {% block extra_controls %}
 {% block extra_controls %}
   {% include 'ipam/inc/toggle_available.html' %}
   {% include 'ipam/inc/toggle_available.html' %}
@@ -13,11 +12,30 @@
 {% endblock %}
 {% endblock %}
 
 
 {% block content %}
 {% block content %}
-  <div class="row">
-    <div class="col col-md-12">
-      {% include 'inc/table_controls.html' with table_modal="PrefixTable_config" %}
-      {% include 'utilities/obj_table.html' with heading='Child Prefixes' bulk_edit_url='ipam:prefix_bulk_edit' bulk_delete_url='ipam:prefix_bulk_delete' parent=prefix %}
+  <form method="post">
+    {% csrf_token %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="PrefixTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
+    </div>
+
+    <div class="noprint bulk-buttons">
+      <div class="bulk-button-group">
+        {% if perms.ipam.change_prefix %}
+          <button type="submit" name="_edit" formaction="{% url 'ipam:prefix_bulk_edit' %}?return_url={% url 'ipam:prefix_prefixes' pk=object.pk %}" class="btn btn-warning btn-sm">
+            <i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
+          </button>
+        {% endif %}
+        {% if perms.ipam.delete_prefix %}
+          <button type="submit" name="_delete" formaction="{% url 'ipam:prefix_bulk_delete' %}?return_url={% url 'ipam:prefix_prefixes' pk=object.pk %}" class="btn btn-danger btn-sm">
+            <i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
+          </button>
+        {% endif %}
+      </div>
     </div>
     </div>
-  </div>
-  {% table_config_form table table_name="PrefixTable" %}
+  </form>
+  {% table_config_form table %}
 {% endblock %}
 {% endblock %}

+ 12 - 4
netbox/templates/ipam/vlan/interfaces.html

@@ -1,9 +1,17 @@
 {% extends 'ipam/vlan/base.html' %}
 {% extends 'ipam/vlan/base.html' %}
+{% load helpers %}
 
 
 {% block content %}
 {% block content %}
-  <div class="row">
-    <div class="col col-md-12">
-      {% include 'utilities/obj_table.html' with heading='Device Interfaces' parent=vlan %}
+  <form method="post">
+    {% csrf_token %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="VLANDevicesTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
     </div>
     </div>
-  </div>
+
+  </form>
+  {% table_config_form table %}
 {% endblock %}
 {% endblock %}

+ 12 - 4
netbox/templates/ipam/vlan/vminterfaces.html

@@ -1,9 +1,17 @@
 {% extends 'ipam/vlan/base.html' %}
 {% extends 'ipam/vlan/base.html' %}
+{% load helpers %}
 
 
 {% block content %}
 {% block content %}
-  <div class="row">
-    <div class="col col-md-12">
-      {% include 'utilities/obj_table.html' with heading='Virtual Machine Interfaces' parent=vlan %}
+  <form method="post">
+    {% csrf_token %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="VLANVirtualMachinesTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
     </div>
     </div>
-  </div>
+
+  </form>
+  {% table_config_form table %}
 {% endblock %}
 {% endblock %}

+ 15 - 16
netbox/templates/virtualization/cluster/devices.html

@@ -3,26 +3,25 @@
 {% load render_table from django_tables2 %}
 {% load render_table from django_tables2 %}
 
 
 {% block content %}
 {% block content %}
-<div class="row">
-  <div class="col col-md-12">
+  <form action="{% url 'virtualization:cluster_remove_devices' pk=object.pk %}" method="post">
+    {% csrf_token %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="DeviceTable_config" %}
+
     <div class="card">
     <div class="card">
-      <h5 class="card-header">
-        Host Devices
-      </h5>
-      <form action="{% url 'virtualization:cluster_remove_devices' pk=object.pk %}" method="post">
-      {% csrf_token %}
-      <div class="card-body table-responsive">
-        {% render_table table 'inc/table.html' %}
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
       </div>
       </div>
-      {% if perms.virtualization.change_cluster %}
-        <div class="card-footer noprint">
+    </div>
+
+    <div class="noprint bulk-buttons">
+      <div class="bulk-button-group">
+        {% if perms.virtualization.change_cluster %}
           <button type="submit" name="_remove" class="btn btn-danger btn-sm">
           <button type="submit" name="_remove" class="btn btn-danger btn-sm">
             <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Remove Devices
             <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Remove Devices
           </button>
           </button>
-        </div>
-      {% endif %}
-      </form>
+        {% endif %}
+      </div>
     </div>
     </div>
-  </div>
-</div>
+  </form>
+  {% table_config_form table %}
 {% endblock %}
 {% endblock %}

+ 23 - 9
netbox/templates/virtualization/cluster/virtual_machines.html

@@ -3,16 +3,30 @@
 {% load render_table from django_tables2 %}
 {% load render_table from django_tables2 %}
 
 
 {% block content %}
 {% block content %}
-<div class="row">
-  <div class="col col-md-12">
+  <form method="post">
+    {% csrf_token %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="VirtualMachineTable_config" %}
+
     <div class="card">
     <div class="card">
-      <h5 class="card-header">
-        Virtual Machines
-      </h5>
-      <div class="card-body table-responsive">
-        {% render_table table 'inc/table.html' %}
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
+    </div>
+
+    <div class="noprint bulk-buttons">
+      <div class="bulk-button-group">
+        {% if perms.virtualization.change_virtualmachine %}
+          <button type="submit" name="_edit" formaction="{% url 'virtualization:virtualmachine_bulk_edit' %}?return_url={% url 'virtualization:cluster_virtualmachines' pk=object.pk %}" class="btn btn-warning btn-sm">
+            <i class="mdi mdi-pencil" aria-hidden="true"></i> Edit
+          </button>
+        {% endif %}
+        {% if perms.virtualization.delete_virtualmachine %}
+          <button type="submit" name="_delete" formaction="{% url 'virtualization:virtualmachine_bulk_delete' %}?return_url={% url 'virtualization:cluster_virtualmachines' pk=object.pk %}" class="btn btn-danger btn-sm">
+            <i class="mdi mdi-trash-can-outline" aria-hidden="true"></i> Delete
+          </button>
+        {% endif %}
       </div>
       </div>
     </div>
     </div>
-  </div>
-</div>
+  </form>
+  {% table_config_form table %}
 {% endblock %}
 {% endblock %}

+ 7 - 4
netbox/templates/virtualization/virtualmachine/interfaces.html

@@ -1,15 +1,18 @@
 {% extends 'virtualization/virtualmachine/base.html' %}
 {% extends 'virtualization/virtualmachine/base.html' %}
 {% load render_table from django_tables2 %}
 {% load render_table from django_tables2 %}
 {% load helpers %}
 {% load helpers %}
-{% load static %}
 
 
 {% block content %}
 {% block content %}
   <form method="post">
   <form method="post">
     {% csrf_token %}
     {% csrf_token %}
-    {% include 'inc/table_controls.html' with table_modal="VirtualMachineVMInterfaceTable_config" %}
-    <div class="table-responsive">
-      {% render_table table 'inc/table.html' %}
+    {% include 'inc/table_controls_htmx.html' with table_modal="VMInterfaceTable_config" %}
+
+    <div class="card">
+      <div class="card-body" id="object_list">
+        {% include 'htmx/table.html' %}
+      </div>
     </div>
     </div>
+
     <div class="noprint">
     <div class="noprint">
         {% if perms.virtualization.change_vminterface %}
         {% if perms.virtualization.change_vminterface %}
             <button type="submit" name="_rename" formaction="{% url 'virtualization:vminterface_bulk_rename' %}?return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}" class="btn btn-warning btn-sm">
             <button type="submit" name="_rename" formaction="{% url 'virtualization:vminterface_bulk_rename' %}?return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}" class="btn btn-warning btn-sm">

+ 4 - 0
netbox/virtualization/views.py

@@ -4,6 +4,7 @@ from django.db.models import Prefetch
 from django.shortcuts import get_object_or_404, redirect, render
 from django.shortcuts import get_object_or_404, redirect, render
 from django.urls import reverse
 from django.urls import reverse
 
 
+from dcim.filtersets import DeviceFilterSet
 from dcim.models import Device
 from dcim.models import Device
 from dcim.tables import DeviceTable
 from dcim.tables import DeviceTable
 from extras.views import ObjectConfigContextView
 from extras.views import ObjectConfigContextView
@@ -165,6 +166,7 @@ class ClusterVirtualMachinesView(generic.ObjectChildrenView):
     queryset = Cluster.objects.all()
     queryset = Cluster.objects.all()
     child_model = VirtualMachine
     child_model = VirtualMachine
     table = tables.VirtualMachineTable
     table = tables.VirtualMachineTable
+    filterset = filtersets.VirtualMachineFilterSet
     template_name = 'virtualization/cluster/virtual_machines.html'
     template_name = 'virtualization/cluster/virtual_machines.html'
 
 
     def get_children(self, request, parent):
     def get_children(self, request, parent):
@@ -180,6 +182,7 @@ class ClusterDevicesView(generic.ObjectChildrenView):
     queryset = Cluster.objects.all()
     queryset = Cluster.objects.all()
     child_model = Device
     child_model = Device
     table = DeviceTable
     table = DeviceTable
+    filterset = DeviceFilterSet
     template_name = 'virtualization/cluster/devices.html'
     template_name = 'virtualization/cluster/devices.html'
 
 
     def get_children(self, request, parent):
     def get_children(self, request, parent):
@@ -345,6 +348,7 @@ class VirtualMachineInterfacesView(generic.ObjectChildrenView):
     queryset = VirtualMachine.objects.all()
     queryset = VirtualMachine.objects.all()
     child_model = VMInterface
     child_model = VMInterface
     table = tables.VMInterfaceTable
     table = tables.VMInterfaceTable
+    filterset = filtersets.VMInterfaceFilterSet
     template_name = 'virtualization/virtualmachine/interfaces.html'
     template_name = 'virtualization/virtualmachine/interfaces.html'
 
 
     def get_children(self, request, parent):
     def get_children(self, request, parent):