diff --git a/CHANGELOG.md b/CHANGELOG.md index 82df28edc..a2acd88dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ v2.5.8 (FUTURE) ## Bug Fixes * [#2705](https://github.com/digitalocean/netbox/issues/2705) - Fix endpoint grouping in API docs +* [#2781](https://github.com/digitalocean/netbox/issues/2781) - Fix filtering of sites/devices/VMs by multiple regions * [#2923](https://github.com/digitalocean/netbox/issues/2923) - Provider filter form's site field should be blank by default * [#2938](https://github.com/digitalocean/netbox/issues/2938) - Enforce deterministic ordering of device components returned by API * [#2939](https://github.com/digitalocean/netbox/issues/2939) - Exclude circuit terminations from API interface connections endpoint diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index d06a65ad3..dda904f1c 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -1,6 +1,5 @@ import django_filters from django.contrib.auth.models import User -from django.core.exceptions import ObjectDoesNotExist from django.db.models import Q from netaddr import EUI from netaddr.core import AddrFormatError @@ -508,12 +507,12 @@ class DeviceFilter(CustomFieldFilterSet): asset_tag = NullableCharFieldFilter() region_id = TreeNodeMultipleChoiceFilter( queryset=Region.objects.all(), - field_name='region__in', + field_name='site__region__in', label='Region (ID)', ) region = TreeNodeMultipleChoiceFilter( queryset=Region.objects.all(), - field_name='region__in', + field_name='site__region__in', to_field_name='slug', label='Region (slug)', ) @@ -613,16 +612,6 @@ class DeviceFilter(CustomFieldFilterSet): Q(comments__icontains=value) ).distinct() - def filter_region(self, queryset, name, value): - try: - region = Region.objects.get(**{name: value}) - except ObjectDoesNotExist: - return queryset.none() - return queryset.filter( - Q(site__region=region) | - Q(site__region__in=region.get_descendants()) - ) - def _mac_address(self, queryset, name, value): value = value.strip() if not value: diff --git a/netbox/virtualization/filters.py b/netbox/virtualization/filters.py index 0b7e57ba7..0e5ff6cd2 100644 --- a/netbox/virtualization/filters.py +++ b/netbox/virtualization/filters.py @@ -7,7 +7,7 @@ from netaddr.core import AddrFormatError from dcim.models import DeviceRole, Interface, Platform, Region, Site from extras.filters import CustomFieldFilterSet from tenancy.models import Tenant -from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter +from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter, TreeNodeMultipleChoiceFilter from .constants import VM_STATUS_CHOICES from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine @@ -119,14 +119,15 @@ class VirtualMachineFilter(CustomFieldFilterSet): queryset=Cluster.objects.all(), label='Cluster (ID)', ) - region_id = django_filters.NumberFilter( - method='filter_region', - field_name='pk', + region_id = TreeNodeMultipleChoiceFilter( + queryset=Region.objects.all(), + field_name='cluster__site__region__in', label='Region (ID)', ) - region = django_filters.CharFilter( - method='filter_region', - field_name='slug', + region = TreeNodeMultipleChoiceFilter( + queryset=Region.objects.all(), + field_name='cluster__site__region__in', + to_field_name='slug', label='Region (slug)', ) site_id = django_filters.ModelMultipleChoiceFilter( @@ -184,16 +185,6 @@ class VirtualMachineFilter(CustomFieldFilterSet): Q(comments__icontains=value) ) - def filter_region(self, queryset, name, value): - try: - region = Region.objects.get(**{name: value}) - except ObjectDoesNotExist: - return queryset.none() - return queryset.filter( - Q(cluster__site__region=region) | - Q(cluster__site__region__in=region.get_descendants()) - ) - class InterfaceFilter(django_filters.FilterSet): q = django_filters.CharFilter(