1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Added 'none' options to filters for optional fields

This commit is contained in:
Jeremy Stretch
2016-09-15 17:12:53 -04:00
parent daadf7a49b
commit 9dea5656ad
10 changed files with 142 additions and 79 deletions

View File

@ -5,6 +5,8 @@ from django.db.models import Q
from dcim.models import Site
from extras.filters import CustomFieldFilterSet
from tenancy.models import Tenant
from utilities.filters import NullableModelMultipleChoiceFilter
from .models import Provider, Circuit, CircuitType
@ -64,12 +66,12 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
to_field_name='slug',
label='Circuit type (slug)',
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
tenant_id = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.ModelMultipleChoiceFilter(
tenant = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
to_field_name='slug',

View File

@ -187,5 +187,6 @@ class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Circuit
type = FilterChoiceField(choices=get_filter_choices(CircuitType, id_field='slug', count_field='circuits'))
provider = FilterChoiceField(choices=get_filter_choices(Provider, id_field='slug', count_field='circuits'))
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='circuits'))
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='circuits',
null_option='None'))
site = FilterChoiceField(choices=get_filter_choices(Site, id_field='slug', count_field='circuits'))

View File

@ -4,6 +4,7 @@ from django.db.models import Q
from extras.filters import CustomFieldFilterSet
from tenancy.models import Tenant
from utilities.filters import NullableModelMultipleChoiceFilter
from .models import (
ConsolePort, ConsoleServerPort, Device, DeviceRole, DeviceType, Interface, InterfaceConnection, Manufacturer,
Platform, PowerOutlet, PowerPort, Rack, RackGroup, RackRole, Site,
@ -15,12 +16,12 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
action='search',
label='Search',
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
tenant_id = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.ModelMultipleChoiceFilter(
tenant = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
to_field_name='slug',
@ -75,34 +76,34 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
to_field_name='slug',
label='Site (slug)',
)
group_id = django_filters.ModelMultipleChoiceFilter(
group_id = NullableModelMultipleChoiceFilter(
name='group',
queryset=RackGroup.objects.all(),
label='Group (ID)',
)
group = django_filters.ModelMultipleChoiceFilter(
group = NullableModelMultipleChoiceFilter(
name='group',
queryset=RackGroup.objects.all(),
to_field_name='slug',
label='Group',
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
tenant_id = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.ModelMultipleChoiceFilter(
tenant = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
to_field_name='slug',
label='Tenant (slug)',
)
role_id = django_filters.ModelMultipleChoiceFilter(
role_id = NullableModelMultipleChoiceFilter(
name='role',
queryset=RackRole.objects.all(),
label='Role (ID)',
)
role = django_filters.ModelMultipleChoiceFilter(
role = NullableModelMultipleChoiceFilter(
name='role',
queryset=RackRole.objects.all(),
to_field_name='slug',
@ -177,12 +178,12 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
to_field_name='slug',
label='Role (slug)',
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
tenant_id = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.ModelMultipleChoiceFilter(
tenant = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
to_field_name='slug',
@ -210,12 +211,12 @@ class DeviceFilter(CustomFieldFilterSet, django_filters.FilterSet):
to_field_name='slug',
label='Device model (slug)',
)
platform_id = django_filters.ModelMultipleChoiceFilter(
platform_id = NullableModelMultipleChoiceFilter(
name='platform',
queryset=Platform.objects.all(),
label='Platform (ID)',
)
platform = django_filters.ModelMultipleChoiceFilter(
platform = NullableModelMultipleChoiceFilter(
name='platform',
queryset=Platform.objects.all(),
to_field_name='slug',

View File

@ -120,7 +120,8 @@ class SiteBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
class SiteFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Site
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='sites'))
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='sites',
null_option='None'))
#
@ -246,10 +247,13 @@ class RackBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
class RackFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Rack
site = FilterChoiceField(choices=get_filter_choices(Site, id_field='slug', count_field='racks'))
group_id = FilterChoiceField(choices=get_filter_choices(RackGroup, select_related=['site'], count_field='racks'),
group_id = FilterChoiceField(choices=get_filter_choices(RackGroup, select_related=['site'], count_field='racks',
null_option='None'),
label='Rack Group')
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='racks'))
role = FilterChoiceField(choices=get_filter_choices(RackRole, id_field='slug', count_field='racks'))
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='racks',
null_option='None'))
role = FilterChoiceField(choices=get_filter_choices(RackRole, id_field='slug', count_field='racks',
null_option='None'))
#
@ -595,11 +599,13 @@ class DeviceFilterForm(BootstrapMixin, CustomFieldFilterForm):
count_field='racks__devices'),
label='Rack Group')
role = FilterChoiceField(choices=get_filter_choices(DeviceRole, id_field='slug', count_field='devices'))
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='devices'))
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='devices',
null_option='None'))
device_type_id = FilterChoiceField(choices=get_filter_choices(DeviceType, select_related=['manufacturer'],
count_field='instances'),
label='Type')
platform = FilterChoiceField(choices=get_filter_choices(Platform, id_field='slug', count_field='devices'))
platform = FilterChoiceField(choices=get_filter_choices(Platform, id_field='slug', count_field='devices',
null_option='None'))
status = forms.NullBooleanField(required=False, widget=forms.Select(choices=FORM_STATUS_CHOICES))

View File

@ -7,6 +7,7 @@ from django.db.models import Q
from dcim.models import Site, Device, Interface
from extras.filters import CustomFieldFilterSet
from tenancy.models import Tenant
from utilities.filters import NullableModelMultipleChoiceFilter
from .models import RIR, Aggregate, VRF, Prefix, IPAddress, VLAN, VLANGroup, Role
@ -21,12 +22,12 @@ class VRFFilter(CustomFieldFilterSet, django_filters.FilterSet):
lookup_type='icontains',
label='Name',
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
tenant_id = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.ModelMultipleChoiceFilter(
tenant = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
to_field_name='slug',
@ -85,29 +86,34 @@ class PrefixFilter(CustomFieldFilterSet, django_filters.FilterSet):
action='search_by_parent',
label='Parent prefix',
)
vrf = django_filters.MethodFilter(
action='_vrf',
vrf = NullableModelMultipleChoiceFilter(
name='vrf',
queryset=VRF.objects.all(),
label='VRF',
)
# Duplicate of `vrf` for backward-compatibility
vrf_id = django_filters.MethodFilter(
action='_vrf',
vrf_id = NullableModelMultipleChoiceFilter(
name='vrf_id',
queryset=VRF.objects.all(),
label='VRF',
)
tenant_id = django_filters.MethodFilter(
action='_tenant_id',
tenant_id = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.MethodFilter(
action='_tenant',
label='Tenant',
tenant = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
to_field_name='slug',
label='Tenant (slug)',
)
site_id = django_filters.ModelMultipleChoiceFilter(
site_id = NullableModelMultipleChoiceFilter(
name='site',
queryset=Site.objects.all(),
label='Site (ID)',
)
site = django_filters.ModelMultipleChoiceFilter(
site = NullableModelMultipleChoiceFilter(
name='site',
queryset=Site.objects.all(),
to_field_name='slug',
@ -122,12 +128,12 @@ class PrefixFilter(CustomFieldFilterSet, django_filters.FilterSet):
name='vlan__vid',
label='VLAN number (1-4095)',
)
role_id = django_filters.ModelMultipleChoiceFilter(
role_id = NullableModelMultipleChoiceFilter(
name='role',
queryset=Role.objects.all(),
label='Role (ID)',
)
role = django_filters.ModelMultipleChoiceFilter(
role = NullableModelMultipleChoiceFilter(
name='role',
queryset=Role.objects.all(),
to_field_name='slug',
@ -136,7 +142,7 @@ class PrefixFilter(CustomFieldFilterSet, django_filters.FilterSet):
class Meta:
model = Prefix
fields = ['family', 'site_id', 'site', 'vrf', 'vrf_id', 'vlan_id', 'vlan_vid', 'status', 'role_id', 'role']
fields = ['family', 'site_id', 'site', 'vlan_id', 'vlan_vid', 'status', 'role_id', 'role']
def search(self, queryset, value):
qs_filter = Q(description__icontains=value)
@ -157,17 +163,6 @@ class PrefixFilter(CustomFieldFilterSet, django_filters.FilterSet):
except AddrFormatError:
return queryset.none()
def _vrf(self, queryset, value):
if str(value) == '':
return queryset
try:
vrf_id = int(value)
except ValueError:
return queryset.none()
if vrf_id == 0:
return queryset.filter(vrf__isnull=True)
return queryset.filter(vrf__pk=value)
def _tenant(self, queryset, value):
if str(value) == '':
return queryset
@ -196,22 +191,27 @@ class IPAddressFilter(CustomFieldFilterSet, django_filters.FilterSet):
action='search_by_parent',
label='Parent prefix',
)
vrf = django_filters.MethodFilter(
action='_vrf',
vrf = NullableModelMultipleChoiceFilter(
name='vrf',
queryset=VRF.objects.all(),
label='VRF',
)
# Duplicate of `vrf` for backward-compatibility
vrf_id = django_filters.MethodFilter(
action='_vrf',
vrf_id = NullableModelMultipleChoiceFilter(
name='vrf_id',
queryset=VRF.objects.all(),
label='VRF',
)
tenant_id = django_filters.MethodFilter(
action='_tenant_id',
tenant_id = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.MethodFilter(
action='_tenant',
label='Tenant',
tenant = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
to_field_name='slug',
label='Tenant (slug)',
)
device_id = django_filters.ModelMultipleChoiceFilter(
name='interface__device',
@ -232,7 +232,7 @@ class IPAddressFilter(CustomFieldFilterSet, django_filters.FilterSet):
class Meta:
model = IPAddress
fields = ['q', 'family', 'vrf_id', 'vrf', 'device_id', 'device', 'interface_id']
fields = ['q', 'family', 'device_id', 'device', 'interface_id']
def search(self, queryset, value):
qs_filter = Q(description__icontains=value)
@ -317,12 +317,12 @@ class VLANFilter(CustomFieldFilterSet, django_filters.FilterSet):
to_field_name='slug',
label='Site (slug)',
)
group_id = django_filters.ModelMultipleChoiceFilter(
group_id = NullableModelMultipleChoiceFilter(
name='group',
queryset=VLANGroup.objects.all(),
label='Group (ID)',
)
group = django_filters.ModelMultipleChoiceFilter(
group = NullableModelMultipleChoiceFilter(
name='group',
queryset=VLANGroup.objects.all(),
to_field_name='slug',
@ -337,23 +337,23 @@ class VLANFilter(CustomFieldFilterSet, django_filters.FilterSet):
name='vid',
label='VLAN number (1-4095)',
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
tenant_id = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
label='Tenant (ID)',
)
tenant = django_filters.ModelMultipleChoiceFilter(
tenant = NullableModelMultipleChoiceFilter(
name='tenant',
queryset=Tenant.objects.all(),
to_field_name='slug',
label='Tenant (slug)',
)
role_id = django_filters.ModelMultipleChoiceFilter(
role_id = NullableModelMultipleChoiceFilter(
name='role',
queryset=Role.objects.all(),
label='Role (ID)',
)
role = django_filters.ModelMultipleChoiceFilter(
role = NullableModelMultipleChoiceFilter(
name='role',
queryset=Role.objects.all(),
to_field_name='slug',

View File

@ -74,7 +74,8 @@ class VRFBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
class VRFFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = VRF
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='vrfs'))
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='vrfs',
null_option='None'))
#
@ -272,13 +273,16 @@ class PrefixFilterForm(BootstrapMixin, CustomFieldFilterForm):
'placeholder': 'Network',
}))
family = forms.ChoiceField(required=False, choices=IP_FAMILY_CHOICES, label='Address Family')
vrf = FilterChoiceField(choices=get_filter_choices(VRF, count_field='prefixes', null_option=(0, 'Global')),
vrf = FilterChoiceField(choices=get_filter_choices(VRF, count_field='prefixes', null_option='Global'),
label='VRF')
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='prefixes'),
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='prefixes',
null_option='None'),
label='Tenant')
status = FilterChoiceField(choices=prefix_status_choices)
site = FilterChoiceField(choices=get_filter_choices(Site, id_field='slug', count_field='prefixes'))
role = FilterChoiceField(choices=get_filter_choices(Role, id_field='slug', count_field='prefixes'))
site = FilterChoiceField(choices=get_filter_choices(Site, id_field='slug', count_field='prefixes',
null_option='None'))
role = FilterChoiceField(choices=get_filter_choices(Role, id_field='slug', count_field='prefixes',
null_option='None'))
expand = forms.BooleanField(required=False, label='Expand prefix hierarchy')
@ -415,8 +419,10 @@ class IPAddressFilterForm(BootstrapMixin, CustomFieldFilterForm):
'placeholder': 'Prefix',
}))
family = forms.ChoiceField(required=False, choices=IP_FAMILY_CHOICES, label='Address Family')
vrf = FilterChoiceField(choices=get_filter_choices(VRF, count_field='ip_addresses'), label='VRF')
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='prefixes'),
vrf = FilterChoiceField(choices=get_filter_choices(VRF, count_field='ip_addresses', null_option='None'),
label='VRF')
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='ip_addresses',
null_option='None'),
label='Tenant')
@ -521,8 +527,10 @@ def vlan_status_choices():
class VLANFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = VLAN
site = FilterChoiceField(choices=get_filter_choices(Site, id_field='slug', count_field='vlans'))
group_id = FilterChoiceField(choices=get_filter_choices(VLANGroup, select_related=['site'], count_field='vlans'),
group_id = FilterChoiceField(choices=get_filter_choices(VLANGroup, select_related=['site'], count_field='vlans',
null_option='None'),
label='VLAN Group')
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='vlans'))
tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='vlans',
null_option='None'))
status = FilterChoiceField(choices=vlan_status_choices)
role = FilterChoiceField(choices=get_filter_choices(Role, id_field='slug', count_field='vlans'))
role = FilterChoiceField(choices=get_filter_choices(Role, id_field='slug', count_field='vlans', null_option='None'))

View File

@ -3,6 +3,7 @@ import django_filters
from django.db.models import Q
from extras.filters import CustomFieldFilterSet
from utilities.filters import NullableModelMultipleChoiceFilter
from .models import Tenant, TenantGroup
@ -11,12 +12,12 @@ class TenantFilter(CustomFieldFilterSet, django_filters.FilterSet):
action='search',
label='Search',
)
group_id = django_filters.ModelMultipleChoiceFilter(
group_id = NullableModelMultipleChoiceFilter(
name='group',
queryset=TenantGroup.objects.all(),
label='Group (ID)',
)
group = django_filters.ModelMultipleChoiceFilter(
group = NullableModelMultipleChoiceFilter(
name='group',
queryset=TenantGroup.objects.all(),
to_field_name='slug',

View File

@ -77,4 +77,5 @@ class TenantBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
class TenantFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Tenant
group = FilterChoiceField(choices=get_filter_choices(TenantGroup, id_field='slug', count_field='tenants'))
group = FilterChoiceField(choices=get_filter_choices(TenantGroup, id_field='slug', count_field='tenants',
null_option='None'))

View File

@ -0,0 +1,43 @@
import django_filters
from django.db.models import Q
class NullableModelMultipleChoiceFilter(django_filters.MultipleChoiceFilter):
def __init__(self, *args, **kwargs):
# Convert the queryset to a list of choices prefixed with a "None" option
queryset = kwargs.pop('queryset')
self.to_field_name = kwargs.pop('to_field_name', 'pk')
kwargs['choices'] = [(0, 'None')] + [(getattr(o, self.to_field_name), o) for o in queryset]
super(NullableModelMultipleChoiceFilter, self).__init__(*args, **kwargs)
def filter(self, qs, value):
value = value or () # Make sure we have an iterable
if self.is_noop(qs, value):
return qs
# Even though not a noop, no point filtering if empty
if not value:
return qs
q = Q()
for v in set(value):
# Filtering on NULL
if v == str(0):
arg = {'{}__isnull'.format(self.name): True}
# Filtering on a related field (e.g. slug)
elif self.to_field_name != 'pk':
arg = {'{}__{}'.format(self.name, self.to_field_name): v}
# Filtering on primary key
else:
arg = {self.name: v}
if self.conjoined:
qs = self.get_method(qs)(**arg)
else:
q |= Q(**arg)
if self.distinct:
return self.get_method(qs)(q).distinct()
return self.get_method(qs)(q)

View File

@ -43,7 +43,7 @@ def get_filter_choices(model, id_field='pk', select_related=[], count_field=None
:param id_field: Field to use as the object identifier
:param select_related: Any related tables to include
:param count_field: The field to use for a child COUNT() (optional)
:param null_option: A (value, label) tuple to include at the beginning of the list serving as "null"
:param null_option: A choice to include at the beginning of the list serving as "null"
"""
queryset = model.objects.all()
if select_related:
@ -54,7 +54,7 @@ def get_filter_choices(model, id_field='pk', select_related=[], count_field=None
else:
choices = [(getattr(obj, id_field), u'{}'.format(obj)) for obj in queryset]
if null_option:
choices = [null_option] + choices
choices = [(0, null_option)] + choices
return choices