diff --git a/validity/api/serializers.py b/validity/api/serializers.py index b28fd6c..22b1939 100644 --- a/validity/api/serializers.py +++ b/validity/api/serializers.py @@ -9,9 +9,10 @@ LocationSerializer, ManufacturerSerializer, PlatformSerializer, + RegionSerializer, SiteSerializer, ) -from dcim.models import Device, DeviceType, Location, Manufacturer, Platform, Site +from dcim.models import Device, DeviceType, Location, Manufacturer, Platform, Region, Site from django.utils import timezone from django.utils.translation import gettext_lazy as _ from extras.api.serializers import TagSerializer @@ -67,6 +68,9 @@ class ComplianceSelectorSerializer(NetBoxModelSerializer): site_filter = SerializedPKRelatedField( serializer=SiteSerializer, many=True, nested=True, required=False, queryset=Site.objects.all() ) + region_filter = SerializedPKRelatedField( + serializer=RegionSerializer, many=True, nested=True, required=False, queryset=Region.objects.all() + ) tenant_filter = SerializedPKRelatedField( serializer=TenantSerializer, many=True, nested=True, required=False, queryset=Tenant.objects.all() ) @@ -88,6 +92,7 @@ class Meta: "status_filter", "location_filter", "site_filter", + "region_filter", "tenant_filter", "dynamic_pairs", "dp_tag_prefix", diff --git a/validity/forms/bulk_import.py b/validity/forms/bulk_import.py index cbbb4fe..6b9c336 100644 --- a/validity/forms/bulk_import.py +++ b/validity/forms/bulk_import.py @@ -1,6 +1,6 @@ from core.models import DataFile, DataSource from dcim.choices import DeviceStatusChoices -from dcim.models import DeviceRole, DeviceType, Location, Manufacturer, Platform, Site +from dcim.models import DeviceRole, DeviceType, Location, Manufacturer, Platform, Region, Site from django.forms import Form from django.utils.translation import gettext_lazy as _ from extras.models import Tag @@ -158,6 +158,12 @@ class ComplianceSelectorImportForm(NetBoxModelImportForm): help_text=_("Site slugs separated by commas, encased with double quotes"), required=False, ) + region_filter = CSVModelMultipleChoiceField( + queryset=Region.objects.all(), + to_field_name="slug", + help_text=_("Region slugs separated by commas, encased with double quotes"), + required=False, + ) tenant_filter = CSVModelMultipleChoiceField( queryset=Tenant.objects.all(), to_field_name="slug", @@ -182,6 +188,7 @@ class Meta: "status_filter", "location_filter", "site_filter", + "region_filter", "tenant_filter", "dynamic_pairs", "dp_tag_prefix", diff --git a/validity/forms/general.py b/validity/forms/general.py index 75add90..0074b86 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, DeviceRole, DeviceType, Location, Manufacturer, Platform, Site +from dcim.models import Device, DeviceRole, DeviceType, Location, Manufacturer, Platform, Region, 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 @@ -56,6 +56,7 @@ class ComplianceSelectorForm(NetBoxModelForm): 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) + region_filter = DynamicModelMultipleChoiceField(queryset=Region.objects.all(), required=False) tenant_filter = DynamicModelMultipleChoiceField(queryset=Tenant.objects.all(), required=False) fieldsets = ( @@ -70,6 +71,7 @@ class ComplianceSelectorForm(NetBoxModelForm): "manufacturer_filter", "platform_filter", "site_filter", + "region_filter", "status_filter", "tag_filter", "tenant_filter", diff --git a/validity/migrations/0014_add_region_to_selector.py b/validity/migrations/0014_add_region_to_selector.py new file mode 100644 index 0000000..c1d9cf9 --- /dev/null +++ b/validity/migrations/0014_add_region_to_selector.py @@ -0,0 +1,18 @@ +# Generated by Django 6.1 on 2026-09-13 22:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('validity', '0013_compliancetest_enabled'), + ] + + operations = [ + migrations.AddField( + model_name='complianceselector', + name='region_filter', + field=models.ManyToManyField(blank=True, related_name='+', to='dcim.region'), + ), + ] diff --git a/validity/models/selector.py b/validity/models/selector.py index b15c4df..837a5e6 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, DeviceRole, DeviceType, Location, Manufacturer, Platform, Site +from dcim.models import Device, DeviceRole, DeviceType, Location, Manufacturer, Platform, Region, Site from django.core.exceptions import ValidationError from django.db import models from django.utils.translation import gettext_lazy as _ @@ -38,6 +38,7 @@ class ComplianceSelector(BaseModel): status_filter = models.CharField(max_length=50, choices=DeviceStatusChoices, blank=True) location_filter = models.ManyToManyField(Location, verbose_name=_("Location Filter"), blank=True, related_name="+") site_filter = models.ManyToManyField(Site, verbose_name=_("Site Filter"), blank=True, related_name="+") + region_filter = models.ManyToManyField(Region, verbose_name=_("Region Filter"), blank=True, related_name="+") tenant_filter = models.ManyToManyField(Tenant, verbose_name=_("Tenant Filter"), blank=True, related_name="+") dynamic_pairs = models.CharField( _("Dynamic Pairs"), max_length=20, choices=DynamicPairsChoices.choices, default="NO" @@ -57,6 +58,7 @@ class ComplianceSelector(BaseModel): "status_filter", "location_filter", "site_filter", + "region_filter", "tenant_filter", "dynamic_pairs", ) @@ -71,6 +73,7 @@ class ComplianceSelector(BaseModel): "status_filter": "status", "location_filter": "location", "site_filter": "site", + "region_filter": "site__region", "tenant_filter": "tenant", } diff --git a/validity/tables.py b/validity/tables.py index 0a13824..68f98b9 100644 --- a/validity/tables.py +++ b/validity/tables.py @@ -31,6 +31,7 @@ class SelectorTable(NetBoxTable): status_filter = BooleanColumn(empty_values=()) location_filter = BooleanColumn(accessor="location_filter__all") site_filter = BooleanColumn(accessor="site_filter__all") + region_filter = BooleanColumn(accessor="region_filter__all") tenant_filter = BooleanColumn(accessor="tenant_filter__all") dynamic_pairs = ChoiceFieldColumn() @@ -48,6 +49,7 @@ class Meta(NetBoxTable.Meta): "status_filter", "location_filter", "site_filter", + "region_filter", "tenant_filter", "dynamic_pairs", ) diff --git a/validity/templates/validity/complianceselector.html b/validity/templates/validity/complianceselector.html index 720c08c..5a1a668 100644 --- a/validity/templates/validity/complianceselector.html +++ b/validity/templates/validity/complianceselector.html @@ -63,6 +63,11 @@