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

This commit is contained in:
Jeremy Stretch 2018-11-12 15:48:58 -05:00
parent b6a256dc5d
commit 0c33af2140
8 changed files with 42 additions and 63 deletions

View File

@ -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
--- ---

View File

@ -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( tag = TagFilter()
name='tags__slug',
)
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( tag = TagFilter()
name='tags__slug',
)
class Meta: class Meta:
model = Circuit model = Circuit

View File

@ -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( tag = TagFilter()
name='tags__slug',
)
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( tag = TagFilter()
name='tags__slug',
)
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( tag = TagFilter()
name='tags__slug',
)
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( tag = TagFilter()
name='tags__slug',
)
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( tag = TagFilter()
name='tags__slug',
)
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( tag = TagFilter()
name='tags__slug',
)
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( tag = TagFilter()
name='tags__slug',
)
class Meta: class Meta:
model = VirtualChassis model = VirtualChassis

View File

@ -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( tag = TagFilter()
name='tags__slug',
)
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( tag = TagFilter()
name='tags__slug',
)
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( tag = TagFilter()
name='tags__slug',
)
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( tag = TagFilter()
name='tags__slug',
)
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( tag = TagFilter()
name='tags__slug',
)
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( tag = TagFilter()
name='tags__slug',
)
class Meta: class Meta:
model = Service model = Service

View File

@ -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( tag = TagFilter()
name='tags__slug',
)
class Meta: class Meta:
model = Secret model = Secret

View File

@ -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( tag = TagFilter()
name='tags__slug',
)
class Meta: class Meta:
model = Tenant model = Tenant

View File

@ -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)

View File

@ -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( tag = TagFilter()
name='tags__slug',
)
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( tag = TagFilter()
name='tags__slug',
)
class Meta: class Meta:
model = VirtualMachine model = VirtualMachine