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

+ 7 - 4
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',

+ 2 - 0
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,

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

+ 11 - 6
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',

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

@@ -1,27 +1,32 @@
 {% 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>
-            {% 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>
+{% endif %}

+ 3 - 2
netbox/tenancy/forms.py

@@ -1,7 +1,7 @@
 from django import forms
 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 .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]
 
 
-class TenantFilterForm(forms.Form, BootstrapMixin):
+class TenantFilterForm(BootstrapMixin, CustomFieldFilterForm):
+    model = Tenant
     group = forms.MultipleChoiceField(required=False, choices=tenant_group_choices,
                                       widget=forms.SelectMultiple(attrs={'size': 8}))