Added custom field support to filter forms

This commit is contained in:
Jeremy Stretch 2016-08-23 12:05:28 -04:00
parent 28b9dda55d
commit 74a5960992
7 changed files with 76 additions and 42 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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