From 16eeff6d88704083288a49e7b8707fb5f57f448a Mon Sep 17 00:00:00 2001 From: Anton M Date: Thu, 23 Jan 2025 00:22:26 +0100 Subject: [PATCH] add role_filter to selector --- validity/api/serializers.py | 5 ++++ validity/api/views.py | 11 +------- validity/forms/general.py | 7 ++++- ...oint.py => 0012_backuppoint_devicerole.py} | 5 ++++ validity/models/selector.py | 5 +++- validity/tables.py | 2 ++ .../validity/complianceselector.html | 26 +++++++++++-------- validity/tests/test_api.py | 2 ++ validity/tests/test_models/test_selector.py | 4 ++- validity/views/selector.py | 13 ++-------- 10 files changed, 45 insertions(+), 35 deletions(-) rename validity/migrations/{0012_backuppoint.py => 0012_backuppoint_devicerole.py} (91%) diff --git a/validity/api/serializers.py b/validity/api/serializers.py index 6109594..72df97d 100644 --- a/validity/api/serializers.py +++ b/validity/api/serializers.py @@ -3,6 +3,7 @@ from core.api.serializers import DataFileSerializer, DataSourceSerializer, JobSerializer from core.models import DataSource from dcim.api.serializers import ( + DeviceRoleSerializer, DeviceSerializer, DeviceTypeSerializer, LocationSerializer, @@ -54,6 +55,9 @@ class ComplianceSelectorSerializer(NetBoxModelSerializer): type_filter = SerializedPKRelatedField( serializer=DeviceTypeSerializer, many=True, nested=True, required=False, queryset=DeviceType.objects.all() ) + role_filter = SerializedPKRelatedField( + serializer=DeviceRoleSerializer, many=True, nested=True, required=False, queryset=DeviceType.objects.all() + ) platform_filter = SerializedPKRelatedField( serializer=PlatformSerializer, many=True, nested=True, required=False, queryset=Platform.objects.all() ) @@ -79,6 +83,7 @@ class Meta: "tag_filter", "manufacturer_filter", "type_filter", + "role_filter", "platform_filter", "status_filter", "location_filter", diff --git a/validity/api/views.py b/validity/api/views.py index bff1a11..2b37cb0 100644 --- a/validity/api/views.py +++ b/validity/api/views.py @@ -38,16 +38,7 @@ def run(self, request): class ComplianceSelectorViewSet(NetBoxModelViewSet): - queryset = models.ComplianceSelector.objects.prefetch_related( - "tag_filter", - "manufacturer_filter", - "type_filter", - "platform_filter", - "location_filter", - "site_filter", - "tenant_filter", - "tags", - ) + queryset = models.ComplianceSelector.objects.prefetch_filters().prefetch_related("tags") serializer_class = serializers.ComplianceSelectorSerializer filterset_class = filtersets.ComplianceSelectorFilterSet diff --git a/validity/forms/general.py b/validity/forms/general.py index 2267f18..35a6bf8 100644 --- a/validity/forms/general.py +++ b/validity/forms/general.py @@ -1,6 +1,6 @@ from core.forms.mixins import SyncedDataMixin from core.models import DataSource -from dcim.models import Device, DeviceType, Location, Manufacturer, Platform, Site +from dcim.models import Device, DeviceRole, DeviceType, Location, Manufacturer, Platform, Site from django.forms import BooleanField, CharField, ChoiceField, IntegerField, Select, Textarea, ValidationError from django.utils.translation import gettext_lazy as _ from extras.forms import ScriptForm @@ -40,6 +40,9 @@ class ComplianceSelectorForm(NetBoxModelForm): type_filter = DynamicModelMultipleChoiceField( queryset=DeviceType.objects.all(), required=False, label=_("Device Type Filter") ) + role_filter = DynamicModelMultipleChoiceField( + queryset=DeviceRole.objects.all(), required=False, label=_("Device Role Filter") + ) platform_filter = DynamicModelMultipleChoiceField(queryset=Platform.objects.all(), required=False) location_filter = DynamicModelMultipleChoiceField(queryset=Location.objects.all(), required=False) site_filter = DynamicModelMultipleChoiceField(queryset=Site.objects.all(), required=False) @@ -52,6 +55,7 @@ class ComplianceSelectorForm(NetBoxModelForm): "filter_operation", "name_filter", "type_filter", + "role_filter", "location_filter", "manufacturer_filter", "platform_filter", @@ -74,6 +78,7 @@ class Meta: "tag_filter", "manufacturer_filter", "type_filter", + "role_filter", "platform_filter", "status_filter", "location_filter", diff --git a/validity/migrations/0012_backuppoint.py b/validity/migrations/0012_backuppoint_devicerole.py similarity index 91% rename from validity/migrations/0012_backuppoint.py rename to validity/migrations/0012_backuppoint_devicerole.py index b091e66..297e22d 100644 --- a/validity/migrations/0012_backuppoint.py +++ b/validity/migrations/0012_backuppoint_devicerole.py @@ -43,4 +43,9 @@ class Migration(migrations.Migration): }, bases=(validity.models.base.SubformMixin, validity.models.base.URLMixin, models.Model), ), + migrations.AddField( + model_name='complianceselector', + name='role_filter', + field=models.ManyToManyField(blank=True, related_name='+', to='dcim.devicerole'), + ), ] diff --git a/validity/models/selector.py b/validity/models/selector.py index b252b83..b15c4df 100644 --- a/validity/models/selector.py +++ b/validity/models/selector.py @@ -4,7 +4,7 @@ from typing import Generator from dcim.choices import DeviceStatusChoices -from dcim.models import Device, DeviceType, Location, Manufacturer, Platform, Site +from dcim.models import Device, DeviceRole, DeviceType, Location, Manufacturer, Platform, Site from django.core.exceptions import ValidationError from django.db import models from django.utils.translation import gettext_lazy as _ @@ -33,6 +33,7 @@ class ComplianceSelector(BaseModel): Manufacturer, verbose_name=_("Manufacturer Filter"), blank=True, related_name="+" ) type_filter = models.ManyToManyField(DeviceType, verbose_name=_("Device Type Filter"), blank=True, related_name="+") + role_filter = models.ManyToManyField(DeviceRole, verbose_name=_("Device Role Filter"), blank=True, related_name="+") platform_filter = models.ManyToManyField(Platform, verbose_name=_("Platform Filter"), blank=True, related_name="+") status_filter = models.CharField(max_length=50, choices=DeviceStatusChoices, blank=True) location_filter = models.ManyToManyField(Location, verbose_name=_("Location Filter"), blank=True, related_name="+") @@ -51,6 +52,7 @@ class ComplianceSelector(BaseModel): "tag_filter", "manufacturer_filter", "type_filter", + "role_filter", "platform_filter", "status_filter", "location_filter", @@ -64,6 +66,7 @@ class ComplianceSelector(BaseModel): "tag_filter": "tags", "manufacturer_filter": "device_type__manufacturer", "type_filter": "device_type", + "role_filter": "role", "platform_filter": "platform", "status_filter": "status", "location_filter": "location", diff --git a/validity/tables.py b/validity/tables.py index 1fd4003..9a6832d 100644 --- a/validity/tables.py +++ b/validity/tables.py @@ -26,6 +26,7 @@ class SelectorTable(NetBoxTable): tag_filter = BooleanColumn(accessor="tag_filter__all") manufacturer_filter = BooleanColumn(accessor="manufacturer_filter__all") type_filter = BooleanColumn(accessor="type_filter__all") + role_filter = BooleanColumn(accessor="role_filter__all") platform_filter = BooleanColumn(accessor="platform_filter__all") status_filter = BooleanColumn(empty_values=()) location_filter = BooleanColumn(accessor="location_filter__all") @@ -42,6 +43,7 @@ class Meta(NetBoxTable.Meta): "tag_filter", "manufacturer_filter", "type_filter", + "role_filter", "platform_filter", "status_filter", "location_filter", diff --git a/validity/templates/validity/complianceselector.html b/validity/templates/validity/complianceselector.html index 3a01c48..720c08c 100644 --- a/validity/templates/validity/complianceselector.html +++ b/validity/templates/validity/complianceselector.html @@ -15,11 +15,11 @@
Compliance Selector
Dynamic Pairs - {{ object | colored_choice:"dynamic_pairs" }} + {{ object | colored_choice:"dynamic_pairs" }} Dynamic Pair Tag Prefix - {{ object.dp_tag_prefix | placeholder }} + {{ object.dp_tag_prefix | placeholder }} @@ -37,39 +37,43 @@
Filters
Device Name Filter - {{ object.name_filter | placeholder }} + {{ object.name_filter | placeholder }} Device Type Filter - {{ object.type_filter.all | linkify_list }} + {{ object.type_filter.all | linkify_list }} + + + Device Role Filter + {{ object.role_filter.all | linkify_list }} Location Filter - {{ object.location_filter.all | linkify_list }} + {{ object.location_filter.all | linkify_list }} Manufacturer Filter - {{ object.manufacturer_filter.all | linkify_list }} + {{ object.manufacturer_filter.all | linkify_list }} Platform Filter - {{ object.platform_filter.all | linkify_list }} + {{ object.platform_filter.all | linkify_list }} Site Filter - {{ object.site_filter.all | linkify_list }} + {{ object.site_filter.all | linkify_list }} Status Filter - {{ object | colored_choice:"status_filter" | placeholder }} + {{ object | colored_choice:"status_filter" | placeholder }} Tag Filter - {{ object.tag_filter.all | linkify_list }} + {{ object.tag_filter.all | linkify_list }} Tenant Filter - {{ object.tenant_filter.all | linkify_list }} + {{ object.tenant_filter.all | linkify_list }} diff --git a/validity/tests/test_api.py b/validity/tests/test_api.py index d3a329e..f19f43f 100644 --- a/validity/tests/test_api.py +++ b/validity/tests/test_api.py @@ -11,6 +11,7 @@ DataFileFactory, DataSourceFactory, DeviceFactory, + DeviceRoleFactory, DeviceTypeFactory, DSBackupJobFactory, LocationFactory, @@ -67,6 +68,7 @@ class TestSelector(ApiPostGetTest): "tag_filter": [TagFactory, TagFactory], "manufacturer_filter": [ManufacturerFactory, ManufacturerFactory], "type_filter": [DeviceTypeFactory, DeviceTypeFactory], + "role_factory": [DeviceRoleFactory], "platform_filter": [PlatformFactory, PlatformFactory], "status_filter": "active", "location_filter": [LocationFactory], diff --git a/validity/tests/test_models/test_selector.py b/validity/tests/test_models/test_selector.py index 365a06e..9d01fe1 100644 --- a/validity/tests/test_models/test_selector.py +++ b/validity/tests/test_models/test_selector.py @@ -4,6 +4,7 @@ from django.db.models import Q from factories import ( DeviceFactory, + DeviceRoleFactory, DeviceTypeFactory, LocationFactory, ManufacturerFactory, @@ -28,13 +29,14 @@ "(AND: ('device_type__manufacturer', ))", ), ("type_filter", [DeviceTypeFactory], "AND", "(AND: ('device_type', ))"), + ("role_filter", [DeviceRoleFactory], "AND", "(AND: ('role', ))"), ("platform_filter", [PlatformFactory], "OR", "(AND: ('platform', ))"), ("status_filter", "ACTIVE", "OR", "(AND: ('status', 'ACTIVE'))"), ("location_filter", [LocationFactory], "AND", "(AND: ('location', ))"), ("site_filter", [SiteFactory], "AND", "(AND: ('site', ))"), ], ) -@pytest.mark.django_db +@pytest.mark.django_db(transaction=True, reset_sequences=True) def test_filter(attr, attr_value, filter_operation, expected_filter): model = SelectorFactory() if isinstance(attr_value, list): diff --git a/validity/views/selector.py b/validity/views/selector.py index 0c534d9..35dc807 100644 --- a/validity/views/selector.py +++ b/validity/views/selector.py @@ -7,7 +7,7 @@ class ComplianceSelectorListView(generic.ObjectListView): - queryset = models.ComplianceSelector.objects.all() + queryset = models.ComplianceSelector.objects.prefetch_filters() table = tables.SelectorTable filterset = filtersets.ComplianceSelectorFilterSet filterset_form = forms.ComplianceSelectorFilterForm @@ -15,16 +15,7 @@ class ComplianceSelectorListView(generic.ObjectListView): @register_model_view(models.ComplianceSelector) class ComplianceSelectorView(TableMixin, generic.ObjectView): - queryset = models.ComplianceSelector.objects.prefetch_related( - "tag_filter", - "manufacturer_filter", - "type_filter", - "platform_filter", - "location_filter", - "site_filter", - "tenant_filter", - "tags", - ) + queryset = models.ComplianceSelector.objects.prefetch_filters().prefetch_related("tags") filterset = DeviceFilterSet object_table_field = "devices" table = tables.DynamicPairsTable