浏览代码

Fixes #2558: Filter on all tags when multiple are passed

Jeremy Stretch 7 年之前
父节点
当前提交
0c33af2140

+ 1 - 0
CHANGELOG.md

@@ -3,6 +3,7 @@ v2.4.8 (FUTURE)
 ## Bug Fixes
 ## Bug Fixes
 
 
 * [#2473](https://github.com/digitalocean/netbox/issues/2473) - Fix encoding of long (>127 character) secrets
 * [#2473](https://github.com/digitalocean/netbox/issues/2473) - Fix encoding of long (>127 character) secrets
+* [#2558](https://github.com/digitalocean/netbox/issues/2558) - Filter on all tags when multiple are passed
 * [#2575](https://github.com/digitalocean/netbox/issues/2575) - Correct model specified for rack roles table
 * [#2575](https://github.com/digitalocean/netbox/issues/2575) - Correct model specified for rack roles table
 
 
 ---
 ---

+ 3 - 7
netbox/circuits/filters.py

@@ -6,7 +6,7 @@ from django.db.models import Q
 from dcim.models import Site
 from dcim.models import Site
 from extras.filters import CustomFieldFilterSet
 from extras.filters import CustomFieldFilterSet
 from tenancy.models import Tenant
 from tenancy.models import Tenant
-from utilities.filters import NumericInFilter
+from utilities.filters import NumericInFilter, TagFilter
 from .constants import CIRCUIT_STATUS_CHOICES
 from .constants import CIRCUIT_STATUS_CHOICES
 from .models import Provider, Circuit, CircuitTermination, CircuitType
 from .models import Provider, Circuit, CircuitTermination, CircuitType
 
 
@@ -28,9 +28,7 @@ class ProviderFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Site (slug)',
         label='Site (slug)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = Provider
         model = Provider
@@ -106,9 +104,7 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Site (slug)',
         label='Site (slug)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = Circuit
         model = Circuit

+ 8 - 22
netbox/dcim/filters.py

@@ -9,7 +9,7 @@ from netaddr.core import AddrFormatError
 
 
 from extras.filters import CustomFieldFilterSet
 from extras.filters import CustomFieldFilterSet
 from tenancy.models import Tenant
 from tenancy.models import Tenant
-from utilities.filters import NullableCharFieldFilter, NumericInFilter
+from utilities.filters import NullableCharFieldFilter, NumericInFilter, TagFilter
 from virtualization.models import Cluster
 from virtualization.models import Cluster
 from .constants import (
 from .constants import (
     DEVICE_STATUS_CHOICES, IFACE_FF_LAG, NONCONNECTABLE_IFACE_TYPES, SITE_STATUS_CHOICES, VIRTUAL_IFACE_TYPES,
     DEVICE_STATUS_CHOICES, IFACE_FF_LAG, NONCONNECTABLE_IFACE_TYPES, SITE_STATUS_CHOICES, VIRTUAL_IFACE_TYPES,
@@ -83,9 +83,7 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Tenant (slug)',
         label='Tenant (slug)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = Site
         model = Site
@@ -196,9 +194,7 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Role (slug)',
         label='Role (slug)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = Rack
         model = Rack
@@ -306,9 +302,7 @@ class DeviceTypeFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Manufacturer (slug)',
         label='Manufacturer (slug)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = DeviceType
         model = DeviceType
@@ -530,9 +524,7 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
         queryset=VirtualChassis.objects.all(),
         queryset=VirtualChassis.objects.all(),
         label='Virtual chassis (ID)',
         label='Virtual chassis (ID)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = Device
         model = Device
@@ -592,9 +584,7 @@ class DeviceComponentFilterSet(django_filters.FilterSet):
         to_field_name='name',
         to_field_name='name',
         label='Device (name)',
         label='Device (name)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
 
 
 class ConsolePortFilter(DeviceComponentFilterSet):
 class ConsolePortFilter(DeviceComponentFilterSet):
@@ -653,9 +643,7 @@ class InterfaceFilter(django_filters.FilterSet):
         method='_mac_address',
         method='_mac_address',
         label='MAC address',
         label='MAC address',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
     vlan_id = django_filters.CharFilter(
     vlan_id = django_filters.CharFilter(
         method='filter_vlan_id',
         method='filter_vlan_id',
         label='Assigned VLAN'
         label='Assigned VLAN'
@@ -797,9 +785,7 @@ class VirtualChassisFilter(django_filters.FilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Tenant (slug)',
         label='Tenant (slug)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = VirtualChassis
         model = VirtualChassis

+ 7 - 19
netbox/ipam/filters.py

@@ -9,7 +9,7 @@ from netaddr.core import AddrFormatError
 from dcim.models import Site, Device, Interface
 from dcim.models import Site, Device, Interface
 from extras.filters import CustomFieldFilterSet
 from extras.filters import CustomFieldFilterSet
 from tenancy.models import Tenant
 from tenancy.models import Tenant
-from utilities.filters import NumericInFilter
+from utilities.filters import NumericInFilter, TagFilter
 from virtualization.models import VirtualMachine
 from virtualization.models import VirtualMachine
 from .constants import IPADDRESS_ROLE_CHOICES, IPADDRESS_STATUS_CHOICES, PREFIX_STATUS_CHOICES, VLAN_STATUS_CHOICES
 from .constants import IPADDRESS_ROLE_CHOICES, IPADDRESS_STATUS_CHOICES, PREFIX_STATUS_CHOICES, VLAN_STATUS_CHOICES
 from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
 from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
@@ -31,9 +31,7 @@ class VRFFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Tenant (slug)',
         label='Tenant (slug)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     def search(self, queryset, name, value):
     def search(self, queryset, name, value):
         if not value.strip():
         if not value.strip():
@@ -73,9 +71,7 @@ class AggregateFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='RIR (slug)',
         label='RIR (slug)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = Aggregate
         model = Aggregate
@@ -174,9 +170,7 @@ class PrefixFilter(CustomFieldFilterSet, django_filters.FilterSet):
         choices=PREFIX_STATUS_CHOICES,
         choices=PREFIX_STATUS_CHOICES,
         null_value=None
         null_value=None
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = Prefix
         model = Prefix
@@ -303,9 +297,7 @@ class IPAddressFilter(CustomFieldFilterSet, django_filters.FilterSet):
     role = django_filters.MultipleChoiceFilter(
     role = django_filters.MultipleChoiceFilter(
         choices=IPADDRESS_ROLE_CHOICES
         choices=IPADDRESS_ROLE_CHOICES
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = IPAddress
         model = IPAddress
@@ -422,9 +414,7 @@ class VLANFilter(CustomFieldFilterSet, django_filters.FilterSet):
         choices=VLAN_STATUS_CHOICES,
         choices=VLAN_STATUS_CHOICES,
         null_value=None
         null_value=None
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = VLAN
         model = VLAN
@@ -466,9 +456,7 @@ class ServiceFilter(django_filters.FilterSet):
         to_field_name='name',
         to_field_name='name',
         label='Virtual machine (name)',
         label='Virtual machine (name)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = Service
         model = Service

+ 2 - 4
netbox/secrets/filters.py

@@ -5,7 +5,7 @@ from django.db.models import Q
 
 
 from dcim.models import Device
 from dcim.models import Device
 from extras.filters import CustomFieldFilterSet
 from extras.filters import CustomFieldFilterSet
-from utilities.filters import NumericInFilter
+from utilities.filters import NumericInFilter, TagFilter
 from .models import Secret, SecretRole
 from .models import Secret, SecretRole
 
 
 
 
@@ -42,9 +42,7 @@ class SecretFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='name',
         to_field_name='name',
         label='Device (name)',
         label='Device (name)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = Secret
         model = Secret

+ 2 - 4
netbox/tenancy/filters.py

@@ -4,7 +4,7 @@ import django_filters
 from django.db.models import Q
 from django.db.models import Q
 
 
 from extras.filters import CustomFieldFilterSet
 from extras.filters import CustomFieldFilterSet
-from utilities.filters import NumericInFilter
+from utilities.filters import NumericInFilter, TagFilter
 from .models import Tenant, TenantGroup
 from .models import Tenant, TenantGroup
 
 
 
 
@@ -31,9 +31,7 @@ class TenantFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Group (slug)',
         label='Group (slug)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = Tenant
         model = Tenant

+ 16 - 0
netbox/utilities/filters.py

@@ -5,6 +5,7 @@ import itertools
 import django_filters
 import django_filters
 from django import forms
 from django import forms
 from django.utils.encoding import force_text
 from django.utils.encoding import force_text
+from taggit.models import Tag
 
 
 
 
 #
 #
@@ -68,3 +69,18 @@ class NullableModelMultipleChoiceField(forms.ModelMultipleChoiceField):
             stripped_value = value
             stripped_value = value
         super(NullableModelMultipleChoiceField, self).clean(stripped_value)
         super(NullableModelMultipleChoiceField, self).clean(stripped_value)
         return value
         return value
+
+
+class TagFilter(django_filters.ModelMultipleChoiceFilter):
+    """
+    Match on one or more assigned tags. If multiple tags are specified (e.g. ?tag=foo&tag=bar), the queryset is filtered
+    to objects matching all tags.
+    """
+    def __init__(self, *args, **kwargs):
+
+        kwargs.setdefault('name', 'tags__slug')
+        kwargs.setdefault('to_field_name', 'slug')
+        kwargs.setdefault('conjoined', True)
+        kwargs.setdefault('queryset', Tag.objects.all())
+
+        super(TagFilter, self).__init__(*args, **kwargs)

+ 3 - 7
netbox/virtualization/filters.py

@@ -9,7 +9,7 @@ from netaddr.core import AddrFormatError
 from dcim.models import DeviceRole, Interface, Platform, Region, Site
 from dcim.models import DeviceRole, Interface, Platform, Region, Site
 from extras.filters import CustomFieldFilterSet
 from extras.filters import CustomFieldFilterSet
 from tenancy.models import Tenant
 from tenancy.models import Tenant
-from utilities.filters import NumericInFilter
+from utilities.filters import NumericInFilter, TagFilter
 from .constants import VM_STATUS_CHOICES
 from .constants import VM_STATUS_CHOICES
 from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 
 
@@ -64,9 +64,7 @@ class ClusterFilter(CustomFieldFilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Site (slug)',
         label='Site (slug)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = Cluster
         model = Cluster
@@ -168,9 +166,7 @@ class VirtualMachineFilter(CustomFieldFilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Platform (slug)',
         label='Platform (slug)',
     )
     )
-    tag = django_filters.CharFilter(
-        name='tags__slug',
-    )
+    tag = TagFilter()
 
 
     class Meta:
     class Meta:
         model = VirtualMachine
         model = VirtualMachine