diff --git a/netbox/circuits/forms.py b/netbox/circuits/forms.py index 488648a3b..0cb7a3016 100644 --- a/netbox/circuits/forms.py +++ b/netbox/circuits/forms.py @@ -2,7 +2,7 @@ from django import forms from django.db.models import Count from dcim.models import Site, Device, Interface, Rack, IFACE_FF_VIRTUAL -from extras.forms import CustomFieldForm, CustomFieldBulkEditForm +from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm from tenancy.forms import bulkedit_tenant_choices from tenancy.models import Tenant from utilities.forms import ( @@ -62,7 +62,8 @@ def provider_site_choices(): return [(s.slug, s.name) for s in site_choices] -class ProviderFilterForm(forms.Form, BootstrapMixin): +class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm): + model = Provider site = forms.MultipleChoiceField(required=False, choices=provider_site_choices, widget=forms.SelectMultiple(attrs={'size': 8})) @@ -208,7 +209,8 @@ def circuit_site_choices(): return [(s.slug, u'{} ({})'.format(s.name, s.circuit_count)) for s in site_choices] -class CircuitFilterForm(forms.Form, BootstrapMixin): +class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm): + model = Circuit type = forms.MultipleChoiceField(required=False, choices=circuit_type_choices) provider = forms.MultipleChoiceField(required=False, choices=circuit_provider_choices, widget=forms.SelectMultiple(attrs={'size': 8})) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 9c4f11d31..8d2e48430 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -3,7 +3,7 @@ import re from django import forms from django.db.models import Count, Q -from extras.forms import CustomFieldForm, CustomFieldBulkEditForm +from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm from ipam.models import IPAddress from tenancy.forms import bulkedit_tenant_choices from tenancy.models import Tenant @@ -122,7 +122,8 @@ def site_tenant_choices(): return [(t.slug, u'{} ({})'.format(t.name, t.site_count)) for t in tenant_choices] -class SiteFilterForm(forms.Form, BootstrapMixin): +class SiteFilterForm(BootstrapMixin, CustomFieldFilterForm): + model = Site tenant = forms.MultipleChoiceField(required=False, choices=site_tenant_choices, widget=forms.SelectMultiple(attrs={'size': 8})) @@ -273,7 +274,8 @@ def rack_role_choices(): return [(r.slug, u'{} ({})'.format(r.name, r.rack_count)) for r in role_choices] -class RackFilterForm(forms.Form, BootstrapMixin): +class RackFilterForm(BootstrapMixin, CustomFieldFilterForm): + model = Rack site = forms.MultipleChoiceField(required=False, choices=rack_site_choices, widget=forms.SelectMultiple(attrs={'size': 8})) group_id = forms.MultipleChoiceField(required=False, choices=rack_group_choices, label='Rack Group', @@ -655,7 +657,8 @@ def device_platform_choices(): return [(p.slug, u'{} ({})'.format(p.name, p.device_count)) for p in platform_choices] -class DeviceFilterForm(forms.Form, BootstrapMixin): +class DeviceFilterForm(BootstrapMixin, CustomFieldFilterForm): + model = Device site = forms.MultipleChoiceField(required=False, choices=device_site_choices, widget=forms.SelectMultiple(attrs={'size': 8})) rack_group_id = forms.MultipleChoiceField(required=False, choices=device_rack_group_choices, label='Rack Group', diff --git a/netbox/extras/filters.py b/netbox/extras/filters.py index d482aa126..447694ce3 100644 --- a/netbox/extras/filters.py +++ b/netbox/extras/filters.py @@ -11,6 +11,8 @@ class CustomFieldFilter(django_filters.Filter): """ def filter(self, queryset, value): + if not value.strip(): + return queryset return queryset.filter( custom_field_values__field__name=self.name, custom_field_values__serialized_value=value, diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py index 9473af20d..1f36e174e 100644 --- a/netbox/extras/forms.py +++ b/netbox/extras/forms.py @@ -4,7 +4,7 @@ from django.contrib.contenttypes.models import ContentType from .models import CF_TYPE_BOOLEAN, CF_TYPE_DATE, CF_TYPE_INTEGER, CF_TYPE_SELECT, CustomField, CustomFieldValue -def get_custom_fields_for_model(content_type, bulk_editing=False): +def get_custom_fields_for_model(content_type, select_empty=False, select_none=True): """ Retrieve all CustomFields applicable to the given ContentType """ @@ -41,9 +41,9 @@ def get_custom_fields_for_model(content_type, bulk_editing=False): # Select elif cf.type == CF_TYPE_SELECT: choices = [(cfc.pk, cfc) for cfc in cf.choices.all()] - if not cf.required: + if select_none and not cf.required: choices = [(0, 'None')] + choices - if bulk_editing: + if select_empty: choices = [(None, '---------')] + choices field = forms.TypedChoiceField(choices=choices, coerce=int, required=cf.required) else: @@ -125,8 +125,24 @@ class CustomFieldBulkEditForm(forms.Form): # Add all applicable CustomFields to the form custom_fields = [] - for name, field in get_custom_fields_for_model(self.obj_type, bulk_editing=True).items(): + for name, field in get_custom_fields_for_model(self.obj_type, select_empty=True).items(): field.required = False self.fields[name] = field custom_fields.append(name) self.custom_fields = custom_fields + + +class CustomFieldFilterForm(forms.Form): + + def __init__(self, *args, **kwargs): + + self.obj_type = ContentType.objects.get_for_model(self.model) + + super(CustomFieldFilterForm, self).__init__(*args, **kwargs) + + # Add all applicable CustomFields to the form + custom_fields = get_custom_fields_for_model(self.obj_type, select_empty=True, select_none=False)\ + .items() + for name, field in custom_fields: + field.required = False + self.fields[name] = field diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index e6a349bbc..938f5aeb6 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -2,7 +2,7 @@ from django import forms from django.db.models import Count from dcim.models import Site, Device, Interface -from extras.forms import CustomFieldForm, CustomFieldBulkEditForm +from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm from tenancy.forms import bulkedit_tenant_choices from tenancy.models import Tenant from utilities.forms import BootstrapMixin, APISelect, Livesearch, CSVDataField, BulkImportForm, SlugField @@ -69,7 +69,8 @@ def vrf_tenant_choices(): return [(t.slug, u'{} ({})'.format(t.name, t.vrf_count)) for t in tenant_choices] -class VRFFilterForm(forms.Form, BootstrapMixin): +class VRFFilterForm(BootstrapMixin, CustomFieldFilterForm): + model = VRF tenant = forms.MultipleChoiceField(required=False, choices=vrf_tenant_choices, widget=forms.SelectMultiple(attrs={'size': 8})) @@ -127,7 +128,8 @@ def aggregate_rir_choices(): return [(r.slug, u'{} ({})'.format(r.name, r.aggregate_count)) for r in rir_choices] -class AggregateFilterForm(forms.Form, BootstrapMixin): +class AggregateFilterForm(BootstrapMixin, CustomFieldFilterForm): + model = Aggregate rir = forms.MultipleChoiceField(required=False, choices=aggregate_rir_choices, label='RIR', widget=forms.SelectMultiple(attrs={'size': 8})) @@ -287,7 +289,8 @@ def prefix_role_choices(): return [(r.slug, u'{} ({})'.format(r.name, r.prefix_count)) for r in role_choices] -class PrefixFilterForm(forms.Form, BootstrapMixin): +class PrefixFilterForm(BootstrapMixin, CustomFieldFilterForm): + model = Prefix parent = forms.CharField(required=False, label='Search Within', widget=forms.TextInput(attrs={ 'placeholder': 'Network', })) @@ -440,7 +443,8 @@ def ipaddress_vrf_choices(): return [(v.pk, u'{} ({})'.format(v.name, v.ipaddress_count)) for v in vrf_choices] -class IPAddressFilterForm(forms.Form, BootstrapMixin): +class IPAddressFilterForm(BootstrapMixin, CustomFieldFilterForm): + model = IPAddress parent = forms.CharField(required=False, label='Search Within', widget=forms.TextInput(attrs={ 'placeholder': 'Prefix', })) @@ -575,7 +579,8 @@ def vlan_role_choices(): return [(r.slug, u'{} ({})'.format(r.name, r.vlan_count)) for r in role_choices] -class VLANFilterForm(forms.Form, BootstrapMixin): +class VLANFilterForm(BootstrapMixin, CustomFieldFilterForm): + model = VLAN site = forms.MultipleChoiceField(required=False, choices=vlan_site_choices, widget=forms.SelectMultiple(attrs={'size': 8})) group_id = forms.MultipleChoiceField(required=False, choices=vlan_group_choices, label='VLAN Group', diff --git a/netbox/templates/inc/filter_panel.html b/netbox/templates/inc/filter_panel.html index 2c73ba0c0..cde76a21c 100644 --- a/netbox/templates/inc/filter_panel.html +++ b/netbox/templates/inc/filter_panel.html @@ -1,27 +1,32 @@ {% load form_helpers %} -