Browse Source

Added custom field support to filter forms

Jeremy Stretch 8 years ago
parent
commit
74a5960992

+ 5 - 3
netbox/circuits/forms.py

@@ -2,7 +2,7 @@ from django import forms
 from django.db.models import Count
 from django.db.models import Count
 
 
 from dcim.models import Site, Device, Interface, Rack, IFACE_FF_VIRTUAL
 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.forms import bulkedit_tenant_choices
 from tenancy.models import Tenant
 from tenancy.models import Tenant
 from utilities.forms import (
 from utilities.forms import (
@@ -62,7 +62,8 @@ def provider_site_choices():
     return [(s.slug, s.name) for s in 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,
     site = forms.MultipleChoiceField(required=False, choices=provider_site_choices,
                                      widget=forms.SelectMultiple(attrs={'size': 8}))
                                      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]
     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)
     type = forms.MultipleChoiceField(required=False, choices=circuit_type_choices)
     provider = forms.MultipleChoiceField(required=False, choices=circuit_provider_choices,
     provider = forms.MultipleChoiceField(required=False, choices=circuit_provider_choices,
                                          widget=forms.SelectMultiple(attrs={'size': 8}))
                                          widget=forms.SelectMultiple(attrs={'size': 8}))

+ 7 - 4
netbox/dcim/forms.py

@@ -3,7 +3,7 @@ import re
 from django import forms
 from django import forms
 from django.db.models import Count, Q
 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 ipam.models import IPAddress
 from tenancy.forms import bulkedit_tenant_choices
 from tenancy.forms import bulkedit_tenant_choices
 from tenancy.models import Tenant
 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]
     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,
     tenant = forms.MultipleChoiceField(required=False, choices=site_tenant_choices,
                                        widget=forms.SelectMultiple(attrs={'size': 8}))
                                        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]
     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,
     site = forms.MultipleChoiceField(required=False, choices=rack_site_choices,
                                      widget=forms.SelectMultiple(attrs={'size': 8}))
                                      widget=forms.SelectMultiple(attrs={'size': 8}))
     group_id = forms.MultipleChoiceField(required=False, choices=rack_group_choices, label='Rack Group',
     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]
     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,
     site = forms.MultipleChoiceField(required=False, choices=device_site_choices,
                                      widget=forms.SelectMultiple(attrs={'size': 8}))
                                      widget=forms.SelectMultiple(attrs={'size': 8}))
     rack_group_id = forms.MultipleChoiceField(required=False, choices=device_rack_group_choices, label='Rack Group',
     rack_group_id = forms.MultipleChoiceField(required=False, choices=device_rack_group_choices, label='Rack Group',

+ 2 - 0
netbox/extras/filters.py

@@ -11,6 +11,8 @@ class CustomFieldFilter(django_filters.Filter):
     """
     """
 
 
     def filter(self, queryset, value):
     def filter(self, queryset, value):
+        if not value.strip():
+            return queryset
         return queryset.filter(
         return queryset.filter(
             custom_field_values__field__name=self.name,
             custom_field_values__field__name=self.name,
             custom_field_values__serialized_value=value,
             custom_field_values__serialized_value=value,

+ 20 - 4
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
 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
     Retrieve all CustomFields applicable to the given ContentType
     """
     """
@@ -41,9 +41,9 @@ def get_custom_fields_for_model(content_type, bulk_editing=False):
         # Select
         # Select
         elif cf.type == CF_TYPE_SELECT:
         elif cf.type == CF_TYPE_SELECT:
             choices = [(cfc.pk, cfc) for cfc in cf.choices.all()]
             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
                 choices = [(0, 'None')] + choices
-            if bulk_editing:
+            if select_empty:
                 choices = [(None, '---------')] + choices
                 choices = [(None, '---------')] + choices
                 field = forms.TypedChoiceField(choices=choices, coerce=int, required=cf.required)
                 field = forms.TypedChoiceField(choices=choices, coerce=int, required=cf.required)
             else:
             else:
@@ -125,8 +125,24 @@ class CustomFieldBulkEditForm(forms.Form):
 
 
         # Add all applicable CustomFields to the form
         # Add all applicable CustomFields to the form
         custom_fields = []
         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
             field.required = False
             self.fields[name] = field
             self.fields[name] = field
             custom_fields.append(name)
             custom_fields.append(name)
         self.custom_fields = custom_fields
         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

+ 11 - 6
netbox/ipam/forms.py

@@ -2,7 +2,7 @@ from django import forms
 from django.db.models import Count
 from django.db.models import Count
 
 
 from dcim.models import Site, Device, Interface
 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.forms import bulkedit_tenant_choices
 from tenancy.models import Tenant
 from tenancy.models import Tenant
 from utilities.forms import BootstrapMixin, APISelect, Livesearch, CSVDataField, BulkImportForm, SlugField
 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]
     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,
     tenant = forms.MultipleChoiceField(required=False, choices=vrf_tenant_choices,
                                        widget=forms.SelectMultiple(attrs={'size': 8}))
                                        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]
     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',
     rir = forms.MultipleChoiceField(required=False, choices=aggregate_rir_choices, label='RIR',
                                     widget=forms.SelectMultiple(attrs={'size': 8}))
                                     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]
     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={
     parent = forms.CharField(required=False, label='Search Within', widget=forms.TextInput(attrs={
         'placeholder': 'Network',
         '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]
     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={
     parent = forms.CharField(required=False, label='Search Within', widget=forms.TextInput(attrs={
         'placeholder': 'Prefix',
         '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]
     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,
     site = forms.MultipleChoiceField(required=False, choices=vlan_site_choices,
                                      widget=forms.SelectMultiple(attrs={'size': 8}))
                                      widget=forms.SelectMultiple(attrs={'size': 8}))
     group_id = forms.MultipleChoiceField(required=False, choices=vlan_group_choices, label='VLAN Group',
     group_id = forms.MultipleChoiceField(required=False, choices=vlan_group_choices, label='VLAN Group',

+ 28 - 23
netbox/templates/inc/filter_panel.html

@@ -1,27 +1,32 @@
 {% load form_helpers %}
 {% load form_helpers %}
 
 
-<div class="panel panel-default">
-    <div class="panel-heading">
-        <span class="fa fa-filter" aria-hidden="true"></span>
-        <strong>Filter</strong>
-    </div>
-    <div class="panel-body">
-        <form action="." method="get" class="form">
-            {% for field in filter_form %}
-                <div class="form-group">
-                    {% if field|widget_type == 'checkboxinput' %}
-                        <label for="{{ field.id_for_label }}">{{ field }} {{ field.label }}</label>
-                    {% else %}
-                        {{ field.label_tag }}
-                        {{ field }}
-                    {% endif %}
+{% if filter_form %}
+    <div class="panel panel-default">
+        <div class="panel-heading">
+            <span class="fa fa-filter" aria-hidden="true"></span>
+            <strong>Filter</strong>
+        </div>
+        <div class="panel-body">
+            <form action="." method="get" class="form">
+                {% for field in filter_form %}
+                    <div class="form-group">
+                        {% if field|widget_type == 'checkboxinput' %}
+                            <label for="{{ field.id_for_label }}">{{ field }} {{ field.label }}</label>
+                        {% else %}
+                            {{ field.label_tag }}
+                            {{ field }}
+                        {% endif %}
+                    </div>
+                {% endfor %}
+                <div class="text-right">
+                    <button type="submit" class="btn btn-primary">
+                        <span class="fa fa-search" aria-hidden="true"></span> Apply
+                    </button>
+                    <a href="." class="btn btn-default">
+                        <span class="fa fa-remove" aria-hidden="true"></span> Clear
+                    </a>
                 </div>
                 </div>
-            {% endfor %}
-            <div class="text-right">
-                <button type="submit" class="btn btn-primary">
-                    <span class="fa fa-search" aria-hidden="true"></span> Apply filters
-                </button>
-            </div>
-        </form>
+            </form>
+        </div>
     </div>
     </div>
-</div>
+{% endif %}

+ 3 - 2
netbox/tenancy/forms.py

@@ -1,7 +1,7 @@
 from django import forms
 from django import forms
 from django.db.models import Count
 from django.db.models import Count
 
 
-from extras.forms import CustomFieldForm, CustomFieldBulkEditForm
+from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
 from utilities.forms import BootstrapMixin, BulkImportForm, CommentField, CSVDataField, SlugField
 from utilities.forms import BootstrapMixin, BulkImportForm, CommentField, CSVDataField, SlugField
 
 
 from .models import Tenant, TenantGroup
 from .models import Tenant, TenantGroup
@@ -79,6 +79,7 @@ def tenant_group_choices():
     return [(g.slug, u'{} ({})'.format(g.name, g.tenant_count)) for g in group_choices]
     return [(g.slug, u'{} ({})'.format(g.name, g.tenant_count)) for g in group_choices]
 
 
 
 
-class TenantFilterForm(forms.Form, BootstrapMixin):
+class TenantFilterForm(BootstrapMixin, CustomFieldFilterForm):
+    model = Tenant
     group = forms.MultipleChoiceField(required=False, choices=tenant_group_choices,
     group = forms.MultipleChoiceField(required=False, choices=tenant_group_choices,
                                       widget=forms.SelectMultiple(attrs={'size': 8}))
                                       widget=forms.SelectMultiple(attrs={'size': 8}))