Implement list filtering forms (#57)

Closes #54

Signed-off-by: wkoot <3715211+wkoot@users.noreply.github.com>
This commit is contained in:
wkoot
2025-02-13 22:16:32 +01:00
committed by GitHub
parent c3ed952b5f
commit 193f04a208
11 changed files with 193 additions and 33 deletions
+1
View File
@@ -60,6 +60,7 @@ class SoftwareProductSerializer(NetBoxModelSerializer):
brief_fields = ("id", "display", "url", "name", "description")
def get_display(self, obj):
# TODO - does not match form and filter views, display manufacturer separately?
return f"{obj.manufacturer} - {obj}"
+63 -4
View File
@@ -1,12 +1,22 @@
from django_filters import CharFilter, ModelMultipleChoiceFilter, MultipleChoiceFilter
from django.db.models import Q
from dcim.models import Device, Manufacturer
from netbox.filtersets import NetBoxModelFilterSet
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation, SoftwareLicense
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation, SoftwareLicense, SoftwareReleaseTypes
from virtualization.models import VirtualMachine, Cluster
class SoftwareProductFilterSet(NetBoxModelFilterSet):
"""Filter capabilities for SoftwareProduct instances."""
name = CharFilter(lookup_expr="icontains")
description = CharFilter(lookup_expr="icontains")
manufacturer = ModelMultipleChoiceFilter(
queryset=Manufacturer.objects.all()
)
class Meta:
model = SoftwareProduct
fields = tuple()
@@ -27,9 +37,24 @@ class SoftwareProductFilterSet(NetBoxModelFilterSet):
class SoftwareProductVersionFilterSet(NetBoxModelFilterSet):
"""Filter capabilities for SoftwareProductVersion instances."""
name = CharFilter(lookup_expr="icontains")
filename = CharFilter(lookup_expr="icontains")
release_type = MultipleChoiceFilter(choices=SoftwareReleaseTypes.choices)
manufacturer = ModelMultipleChoiceFilter(
field_name="software_product__manufacturer",
queryset=Manufacturer.objects.all(),
)
software_product = ModelMultipleChoiceFilter(
queryset=SoftwareProduct.objects.all(),
label="Software Product",
)
class Meta:
model = SoftwareProductVersion
fields = ("software_product",)
fields = tuple()
def search(self, queryset, name, value):
"""Perform the filtered search."""
@@ -47,9 +72,27 @@ class SoftwareProductVersionFilterSet(NetBoxModelFilterSet):
class SoftwareProductInstallationFilterSet(NetBoxModelFilterSet):
"""Filter capabilities for SoftwareProductInstallation instances."""
device = ModelMultipleChoiceFilter(
queryset=Device.objects.all()
)
virtualmachine = ModelMultipleChoiceFilter(
queryset=VirtualMachine.objects.all(),
label="Virtual Machine",
)
cluster = ModelMultipleChoiceFilter(
queryset=Cluster.objects.all()
)
software_product = ModelMultipleChoiceFilter(
queryset=SoftwareProduct.objects.all(),
label="Software Product",
)
version = ModelMultipleChoiceFilter(
queryset=SoftwareProductVersion.objects.all()
)
class Meta:
model = SoftwareProductInstallation
fields = ("software_product",)
fields = tuple()
def search(self, queryset, name, value):
"""Perform the filtered search."""
@@ -67,9 +110,25 @@ class SoftwareProductInstallationFilterSet(NetBoxModelFilterSet):
class SoftwareLicenseFilterSet(NetBoxModelFilterSet):
"""Filter capabilities for SoftwareLicense instances."""
name = CharFilter(lookup_expr="icontains")
description = CharFilter(lookup_expr="icontains")
type = CharFilter(lookup_expr="icontains")
stored_location = CharFilter(lookup_expr="icontains")
software_product = ModelMultipleChoiceFilter(
queryset=SoftwareProduct.objects.all(),
label="Software Product",
)
version = ModelMultipleChoiceFilter(
queryset=SoftwareProductVersion.objects.all()
)
installation = ModelMultipleChoiceFilter(
queryset=SoftwareProductInstallation.objects.all()
)
class Meta:
model = SoftwareLicense
fields = tuple()
fields = ("support",)
def search(self, queryset, name, value):
"""Perform the filtered search."""
+30 -3
View File
@@ -1,9 +1,10 @@
from django.forms import DateField
from django.forms import CharField, DateField, ChoiceField
from django.urls import reverse_lazy
from netbox.forms import NetBoxModelForm, NetBoxModelFilterSetForm, NetBoxModelImportForm
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareProductInstallation, SoftwareLicense
from utilities.forms.fields import CommentField, DynamicModelChoiceField, TagFilterField, LaxURLField
from utilities.forms.constants import BOOLEAN_WITH_BLANK_CHOICES
from utilities.forms.fields import CommentField, DynamicModelChoiceField, TagFilterField, LaxURLField, DynamicModelMultipleChoiceField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import APISelect, DatePicker
@@ -18,6 +19,7 @@ class SoftwareLicenseForm(NetBoxModelForm):
software_product = DynamicModelChoiceField(
queryset=SoftwareProduct.objects.all(),
required=True,
label="Software Product",
widget=APISelect(attrs={"data-url": reverse_lazy("plugins-api:netbox_slm-api:softwareproduct-list")}),
)
version = DynamicModelChoiceField(
@@ -63,9 +65,34 @@ class SoftwareLicenseForm(NetBoxModelForm):
class SoftwareLicenseFilterForm(NetBoxModelFilterSetForm):
model = SoftwareLicense
fieldsets = (FieldSet(None, ("q", "tag")),)
fieldsets = (
FieldSet("q", "filter_id", "tag"),
FieldSet("name", "description", "type", "stored_location", "support", "software_product", "version", "installation"),
)
selector_fields = ("q", "filter_id", "name")
tag = TagFilterField(model)
name = CharField(required=False)
description = CharField(required=False)
type = CharField(required=False)
stored_location = CharField(required=False)
support = ChoiceField(required=False, choices=BOOLEAN_WITH_BLANK_CHOICES)
software_product = DynamicModelMultipleChoiceField(
queryset=SoftwareProduct.objects.all(),
required=False,
label="Software Product",
)
version = DynamicModelMultipleChoiceField(
queryset=SoftwareProductVersion.objects.all(),
required=False,
)
installation = DynamicModelMultipleChoiceField(
queryset=SoftwareProductInstallation.objects.all(),
required=False,
)
class SoftwareLicenseBulkImportForm(NetBoxModelImportForm):
class Meta:
+16 -2
View File
@@ -1,7 +1,9 @@
from django.forms import CharField
from dcim.models import Manufacturer
from netbox.forms import NetBoxModelForm, NetBoxModelFilterSetForm, NetBoxModelImportForm
from netbox_slm.models import SoftwareProduct
from utilities.forms.fields import CommentField, DynamicModelChoiceField, TagFilterField
from utilities.forms.fields import CommentField, DynamicModelChoiceField, TagFilterField, DynamicModelMultipleChoiceField
from utilities.forms.rendering import FieldSet
@@ -28,9 +30,21 @@ class SoftwareProductForm(NetBoxModelForm):
class SoftwareProductFilterForm(NetBoxModelFilterSetForm):
model = SoftwareProduct
fieldsets = (FieldSet(None, ("q", "tag")),)
fieldsets = (
FieldSet("q", "filter_id", "tag"),
FieldSet("name", "description", "manufacturer"),
)
selector_fields = ("q", "filter_id", "name")
tag = TagFilterField(model)
name = CharField(required=False)
description = CharField(required=False)
manufacturer = DynamicModelMultipleChoiceField(
queryset=Manufacturer.objects.all(),
required=False,
)
class SoftwareProductBulkImportForm(NetBoxModelImportForm):
class Meta:
@@ -4,7 +4,7 @@ from django.urls import reverse_lazy
from dcim.models import Device
from netbox.forms import NetBoxModelForm, NetBoxModelFilterSetForm, NetBoxModelImportForm
from netbox_slm.models import SoftwareProductInstallation, SoftwareProduct, SoftwareProductVersion
from utilities.forms.fields import CommentField, DynamicModelChoiceField, TagFilterField
from utilities.forms.fields import CommentField, DynamicModelChoiceField, TagFilterField, DynamicModelMultipleChoiceField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import APISelect
from virtualization.models import VirtualMachine, Cluster
@@ -16,11 +16,16 @@ class SoftwareProductInstallationForm(NetBoxModelForm):
comments = CommentField()
device = DynamicModelChoiceField(queryset=Device.objects.all(), required=False)
virtualmachine = DynamicModelChoiceField(queryset=VirtualMachine.objects.all(), required=False)
virtualmachine = DynamicModelChoiceField(
queryset=VirtualMachine.objects.all(),
required=False,
label="Virtual Machine",
)
cluster = DynamicModelChoiceField(queryset=Cluster.objects.all(), required=False)
software_product = DynamicModelChoiceField(
queryset=SoftwareProduct.objects.all(),
required=True,
label="Software Product",
widget=APISelect(attrs={"data-url": reverse_lazy("plugins-api:netbox_slm-api:softwareproduct-list")}),
)
version = DynamicModelChoiceField(
@@ -57,9 +62,37 @@ class SoftwareProductInstallationForm(NetBoxModelForm):
class SoftwareProductInstallationFilterForm(NetBoxModelFilterSetForm):
model = SoftwareProductInstallation
fieldsets = (FieldSet(None, ("q", "tag")),)
fieldsets = (
FieldSet("q", "filter_id", "tag"),
FieldSet("device", "virtualmachine", "cluster", "software_product", "version"),
)
selector_fields = ("filter_id", "q")
tag = TagFilterField(model)
device = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(),
required=False,
)
virtualmachine = DynamicModelMultipleChoiceField(
queryset=VirtualMachine.objects.all(),
required=False,
label="Virtual Machine",
)
cluster = DynamicModelMultipleChoiceField(
queryset=Cluster.objects.all(),
required=False,
)
software_product = DynamicModelMultipleChoiceField(
queryset=SoftwareProduct.objects.all(),
required=False,
label="Software Product",
)
version = DynamicModelMultipleChoiceField(
queryset=SoftwareProductVersion.objects.all(),
required=False,
)
class SoftwareProductInstallationBulkImportForm(NetBoxModelImportForm):
class Meta:
+27 -4
View File
@@ -1,9 +1,10 @@
from django.forms import DateField
from django.forms import DateField, CharField, MultipleChoiceField
from django.urls import reverse_lazy
from dcim.models import Manufacturer
from netbox.forms import NetBoxModelForm, NetBoxModelFilterSetForm, NetBoxModelImportForm
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion
from utilities.forms.fields import CommentField, DynamicModelChoiceField, TagFilterField
from netbox_slm.models import SoftwareProduct, SoftwareProductVersion, SoftwareReleaseTypes
from utilities.forms.fields import CommentField, DynamicModelChoiceField, TagFilterField, DynamicModelMultipleChoiceField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import APISelect, DatePicker
@@ -18,6 +19,8 @@ class SoftwareProductVersionForm(NetBoxModelForm):
software_product = DynamicModelChoiceField(
queryset=SoftwareProduct.objects.all(),
required=True,
label="Software Product",
widget=APISelect(attrs={"data-url": reverse_lazy("plugins-api:netbox_slm-api:softwareproduct-list")}),
)
@@ -40,9 +43,29 @@ class SoftwareProductVersionForm(NetBoxModelForm):
class SoftwareProductVersionFilterForm(NetBoxModelFilterSetForm):
model = SoftwareProductVersion
fieldsets = (FieldSet(None, ("q", "tag")),)
fieldsets = (
FieldSet("q", "filter_id", "tag"),
FieldSet("name", "filename", "release_type", "manufacturer", "software_product"),
)
selector_fields = ("q", "filter_id", "name")
tag = TagFilterField(model)
name = CharField(required=False)
filename = CharField(required=False)
release_type = MultipleChoiceField(required=False, choices=SoftwareReleaseTypes.choices)
manufacturer = DynamicModelMultipleChoiceField(
queryset=Manufacturer.objects.all(),
required=False,
)
software_product = DynamicModelMultipleChoiceField(
queryset=SoftwareProduct.objects.all(),
required=False,
label="Software Product",
)
class SoftwareProductVersionBulkImportForm(NetBoxModelImportForm):
class Meta:
+2
View File
@@ -145,6 +145,7 @@ class SoftwareProductInstallationTable(NetBoxTable):
return queryset_union.order_by(f"{'-' if is_descending else ''}render_type"), True
def render_software_product(self, value, **kwargs):
# TODO - does not match form and filter views, display manufacturer separately?
return f"{kwargs['record'].software_product.manufacturer.name} - {value}"
@@ -190,6 +191,7 @@ class SoftwareLicenseTable(NetBoxTable):
)
def render_software_product(self, value, **kwargs):
# TODO - does not match form and filter views, display manufacturer separately?
return f"{kwargs['record'].software_product.manufacturer.name} - {value}"
def render_installation(self, **kwargs):
@@ -20,6 +20,18 @@
<th scope="row">Description</th>
<td>{{ object.description }}</td>
</tr>
<tr>
<th scope="row">Software Product</th>
<td>{{ object.software_product|linkify }}</td>
</tr>
<tr>
<th scope="row">Version</th>
<td>{{ object.version|linkify }}</td>
</tr>
<tr>
<th scope="row">Installation</th>
<td>{{ object.installation|linkify }}</td>
</tr>
<tr>
<th scope="row">Type</th>
<td>{{ object.type }}</td>
@@ -40,14 +52,6 @@
<th scope="row">Expiration date</th>
<td>{{ object.expiration_date }}</td>
</tr>
<tr>
<th scope="row">Software Product</th>
<td>{{ object.software_product|linkify }}</td>
</tr>
<tr>
<th scope="row">Version</th>
<td>{{ object.version|linkify }}</td>
</tr>
<tr>
<th scope="row">Support</th>
<td>{{ object.support }}</td>
@@ -56,10 +60,6 @@
<th scope="row">License amount</th>
<td>{{ object.license_amount }}</td>
</tr>
<tr>
<th scope="row">Installation</th>
<td>{{ object.installation|linkify }}</td>
</tr>
</table>
</div>
{% include 'inc/panels/custom_fields.html' %}
@@ -19,7 +19,7 @@
</tr>
{% elif object.virtualmachine %}
<tr>
<th scope="row">Virtualmachine</th>
<th scope="row">Virtual Machine</th>
<td>{{ object.virtualmachine|linkify }}</td>
</tr>
{% else %}
@@ -16,14 +16,14 @@
<th scope="row">Name</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">Software Product</th>
<td>{{ object.software_product|linkify }}</td>
</tr>
<tr>
<th scope="row">Manufacturer</th>
<td>{{ object.software_product.manufacturer|linkify }}</td>
</tr>
<tr>
<th scope="row">Software Product</th>
<td>{{ object.software_product|linkify }}</td>
</tr>
<tr>
<th scope="row">Release date</th>
<td>{{ object.release_date }}</td>