diff --git a/netbox/circuits/forms.py b/netbox/circuits/forms.py index 0cb7a3016..c934ab630 100644 --- a/netbox/circuits/forms.py +++ b/netbox/circuits/forms.py @@ -1,12 +1,12 @@ 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, CustomFieldFilterForm from tenancy.forms import bulkedit_tenant_choices from tenancy.models import Tenant from utilities.forms import ( - APISelect, BootstrapMixin, BulkImportForm, CommentField, CSVDataField, Livesearch, SmallTextarea, SlugField, + APISelect, BootstrapMixin, BulkImportForm, CommentField, CSVDataField, FilterChoiceField, Livesearch, SmallTextarea, + SlugField, get_filter_choices, ) from .models import Circuit, CircuitType, Provider @@ -57,15 +57,9 @@ class ProviderBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): comments = CommentField() -def provider_site_choices(): - site_choices = Site.objects.all() - return [(s.slug, s.name) for s in site_choices] - - class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm): model = Provider - site = forms.MultipleChoiceField(required=False, choices=provider_site_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) + site = FilterChoiceField(choices=get_filter_choices(Site, id_field='slug')) # @@ -189,32 +183,9 @@ class CircuitBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): comments = CommentField() -def circuit_type_choices(): - type_choices = CircuitType.objects.annotate(circuit_count=Count('circuits')) - return [(t.slug, u'{} ({})'.format(t.name, t.circuit_count)) for t in type_choices] - - -def circuit_provider_choices(): - provider_choices = Provider.objects.annotate(circuit_count=Count('circuits')) - return [(p.slug, u'{} ({})'.format(p.name, p.circuit_count)) for p in provider_choices] - - -def circuit_tenant_choices(): - tenant_choices = Tenant.objects.annotate(circuit_count=Count('circuits')) - return [(t.slug, u'{} ({})'.format(t.name, t.circuit_count)) for t in tenant_choices] - - -def circuit_site_choices(): - site_choices = Site.objects.annotate(circuit_count=Count('circuits')) - return [(s.slug, u'{} ({})'.format(s.name, s.circuit_count)) for s in site_choices] - - 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})) - tenant = forms.MultipleChoiceField(required=False, choices=circuit_tenant_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) - site = forms.MultipleChoiceField(required=False, choices=circuit_site_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) + 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')) + site = FilterChoiceField(choices=get_filter_choices(Site, id_field='slug', count_field='circuits')) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 8d2e48430..7625955b4 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -1,7 +1,7 @@ import re from django import forms -from django.db.models import Count, Q +from django.db.models import Q from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm from ipam.models import IPAddress @@ -9,7 +9,8 @@ from tenancy.forms import bulkedit_tenant_choices from tenancy.models import Tenant from utilities.forms import ( APISelect, add_blank_choice, BootstrapMixin, BulkImportForm, CommentField, CSVDataField, ExpandableNameField, - FlexibleModelChoiceField, Livesearch, SelectWithDisabled, SmallTextarea, SlugField, + FilterChoiceField, FlexibleModelChoiceField, Livesearch, SelectWithDisabled, SmallTextarea, SlugField, + get_filter_choices ) from .models import ( @@ -117,15 +118,9 @@ class SiteBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): tenant = forms.TypedChoiceField(choices=bulkedit_tenant_choices, coerce=int, required=False, label='Tenant') -def site_tenant_choices(): - tenant_choices = Tenant.objects.annotate(site_count=Count('sites')) - return [(t.slug, u'{} ({})'.format(t.name, t.site_count)) for t in tenant_choices] - - class SiteFilterForm(BootstrapMixin, CustomFieldFilterForm): model = Site - tenant = forms.MultipleChoiceField(required=False, choices=site_tenant_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) + tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='sites')) # @@ -140,14 +135,8 @@ class RackGroupForm(forms.ModelForm, BootstrapMixin): fields = ['site', 'name', 'slug'] -def rackgroup_site_choices(): - site_choices = Site.objects.annotate(rack_count=Count('rack_groups')) - return [(s.slug, u'{} ({})'.format(s.name, s.rack_count)) for s in site_choices] - - class RackGroupFilterForm(forms.Form, BootstrapMixin): - site = forms.MultipleChoiceField(required=False, choices=rackgroup_site_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) + site = FilterChoiceField(choices=get_filter_choices(Site, id_field='slug', count_field='rack_groups')) # @@ -254,36 +243,13 @@ class RackBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): comments = CommentField() -def rack_site_choices(): - site_choices = Site.objects.annotate(rack_count=Count('racks')) - return [(s.slug, u'{} ({})'.format(s.name, s.rack_count)) for s in site_choices] - - -def rack_group_choices(): - group_choices = RackGroup.objects.select_related('site').annotate(rack_count=Count('racks')) - return [(g.pk, u'{} ({})'.format(g, g.rack_count)) for g in group_choices] - - -def rack_tenant_choices(): - tenant_choices = Tenant.objects.annotate(rack_count=Count('racks')) - return [(t.slug, u'{} ({})'.format(t.name, t.rack_count)) for t in tenant_choices] - - -def rack_role_choices(): - role_choices = RackRole.objects.annotate(rack_count=Count('racks')) - return [(r.slug, u'{} ({})'.format(r.name, r.rack_count)) for r in role_choices] - - 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', - widget=forms.SelectMultiple(attrs={'size': 8})) - tenant = forms.MultipleChoiceField(required=False, choices=rack_tenant_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) - role = forms.MultipleChoiceField(required=False, choices=rack_role_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) + 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'), + 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')) # @@ -317,14 +283,9 @@ class DeviceTypeBulkEditForm(forms.Form, BootstrapMixin): u_height = forms.IntegerField(min_value=1, required=False) -def devicetype_manufacturer_choices(): - manufacturer_choices = Manufacturer.objects.annotate(devicetype_count=Count('device_types')) - return [(m.slug, u'{} ({})'.format(m.name, m.devicetype_count)) for m in manufacturer_choices] - - class DeviceTypeFilterForm(forms.Form, BootstrapMixin): - manufacturer = forms.MultipleChoiceField(required=False, choices=devicetype_manufacturer_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) + manufacturer = FilterChoiceField(choices=get_filter_choices(Manufacturer, id_field='slug', + count_field='device_types')) # @@ -627,49 +588,18 @@ class DeviceBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): serial = forms.CharField(max_length=50, required=False, label='Serial Number') -def device_site_choices(): - site_choices = Site.objects.annotate(device_count=Count('racks__devices')) - return [(s.slug, u'{} ({})'.format(s.name, s.device_count)) for s in site_choices] - - -def device_rack_group_choices(): - group_choices = RackGroup.objects.select_related('site').annotate(device_count=Count('racks__devices')) - return [(g.pk, u'{} ({})'.format(g, g.device_count)) for g in group_choices] - - -def device_role_choices(): - role_choices = DeviceRole.objects.annotate(device_count=Count('devices')) - return [(r.slug, u'{} ({})'.format(r.name, r.device_count)) for r in role_choices] - - -def device_tenant_choices(): - tenant_choices = Tenant.objects.annotate(device_count=Count('devices')) - return [(t.slug, u'{} ({})'.format(t.name, t.device_count)) for t in tenant_choices] - - -def device_type_choices(): - type_choices = DeviceType.objects.select_related('manufacturer').annotate(device_count=Count('instances')) - return [(t.pk, u'{} ({})'.format(t, t.device_count)) for t in type_choices] - - -def device_platform_choices(): - platform_choices = Platform.objects.annotate(device_count=Count('devices')) - return [(p.slug, u'{} ({})'.format(p.name, p.device_count)) for p in platform_choices] - - 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', - widget=forms.SelectMultiple(attrs={'size': 8})) - role = forms.MultipleChoiceField(required=False, choices=device_role_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) - tenant = forms.MultipleChoiceField(required=False, choices=device_tenant_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) - device_type_id = forms.MultipleChoiceField(required=False, choices=device_type_choices, label='Type', - widget=forms.SelectMultiple(attrs={'size': 8})) - platform = forms.MultipleChoiceField(required=False, choices=device_platform_choices) + site = FilterChoiceField(choices=get_filter_choices(Site, id_field='slug', count_field='racks__devices')) + rack_group_id = FilterChoiceField(choices=get_filter_choices(RackGroup, select_related=['site'], + 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')) + 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')) status = forms.NullBooleanField(required=False, widget=forms.Select(choices=FORM_STATUS_CHOICES)) diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index 5e08ca854..073c06f27 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -5,7 +5,10 @@ from dcim.models import Site, Device, Interface 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 +from utilities.forms import ( + APISelect, BootstrapMixin, CSVDataField, BulkImportForm, FilterChoiceField, Livesearch, SlugField, + get_filter_choices, +) from .models import ( Aggregate, IPAddress, Prefix, PREFIX_STATUS_CHOICES, RIR, Role, VLAN, VLANGroup, VLAN_STATUS_CHOICES, VRF, @@ -69,15 +72,9 @@ class VRFBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): description = forms.CharField(max_length=100, required=False) -def vrf_tenant_choices(): - tenant_choices = Tenant.objects.annotate(vrf_count=Count('vrfs')) - return [(t.slug, u'{} ({})'.format(t.name, t.vrf_count)) for t in tenant_choices] - - class VRFFilterForm(BootstrapMixin, CustomFieldFilterForm): model = VRF - tenant = forms.MultipleChoiceField(required=False, choices=vrf_tenant_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) + tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='vrfs')) # @@ -128,16 +125,10 @@ class AggregateBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): description = forms.CharField(max_length=100, required=False) -def aggregate_rir_choices(): - rir_choices = RIR.objects.annotate(aggregate_count=Count('aggregates')) - return [(r.slug, u'{} ({})'.format(r.name, r.aggregate_count)) for r in rir_choices] - - class AggregateFilterForm(BootstrapMixin, CustomFieldFilterForm): model = Aggregate family = forms.ChoiceField(required=False, choices=IP_FAMILY_CHOICES, label='Address Family') - rir = forms.MultipleChoiceField(required=False, choices=aggregate_rir_choices, label='RIR', - widget=forms.SelectMultiple(attrs={'size': 8})) + rir = FilterChoiceField(choices=get_filter_choices(RIR, id_field='slug', count_field='aggregates'), label='RIR') # @@ -268,21 +259,6 @@ class PrefixBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): description = forms.CharField(max_length=100, required=False) -def prefix_vrf_choices(): - vrf_choices = VRF.objects.annotate(prefix_count=Count('prefixes')) - return [(v.pk, u'{} ({})'.format(v.name, v.prefix_count)) for v in vrf_choices] - - -def tenant_choices(): - tenant_choices = Tenant.objects.all() - return [(t.slug, t.name) for t in tenant_choices] - - -def prefix_site_choices(): - site_choices = Site.objects.annotate(prefix_count=Count('prefixes')) - return [(s.slug, u'{} ({})'.format(s.name, s.prefix_count)) for s in site_choices] - - def prefix_status_choices(): status_counts = {} for status in Prefix.objects.values('status').annotate(count=Count('status')).order_by('status'): @@ -290,27 +266,18 @@ def prefix_status_choices(): return [(s[0], u'{} ({})'.format(s[1], status_counts.get(s[0], 0))) for s in PREFIX_STATUS_CHOICES] -def prefix_role_choices(): - role_choices = Role.objects.annotate(prefix_count=Count('prefixes')) - return [(r.slug, u'{} ({})'.format(r.name, r.prefix_count)) for r in role_choices] - - class PrefixFilterForm(BootstrapMixin, CustomFieldFilterForm): model = Prefix parent = forms.CharField(required=False, label='Search Within', widget=forms.TextInput(attrs={ 'placeholder': 'Network', })) family = forms.ChoiceField(required=False, choices=IP_FAMILY_CHOICES, label='Address Family') - vrf = forms.MultipleChoiceField(required=False, choices=prefix_vrf_choices, label='VRF', - widget=forms.SelectMultiple(attrs={'size': 6})) - tenant = forms.MultipleChoiceField(required=False, choices=tenant_choices, label='Tenant', - widget=forms.SelectMultiple(attrs={'size': 6})) - status = forms.MultipleChoiceField(required=False, choices=prefix_status_choices, - widget=forms.SelectMultiple(attrs={'size': 6})) - site = forms.MultipleChoiceField(required=False, choices=prefix_site_choices, - widget=forms.SelectMultiple(attrs={'size': 6})) - role = forms.MultipleChoiceField(required=False, choices=prefix_role_choices, - widget=forms.SelectMultiple(attrs={'size': 6})) + vrf = FilterChoiceField(choices=get_filter_choices(VRF, count_field='prefixes'), label='VRF') + tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='prefixes'), + 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')) expand = forms.BooleanField(required=False, label='Expand prefix hierarchy') @@ -441,21 +408,15 @@ class IPAddressBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): description = forms.CharField(max_length=100, required=False) -def ipaddress_vrf_choices(): - vrf_choices = VRF.objects.annotate(ipaddress_count=Count('ip_addresses')) - return [(v.pk, u'{} ({})'.format(v.name, v.ipaddress_count)) for v in vrf_choices] - - class IPAddressFilterForm(BootstrapMixin, CustomFieldFilterForm): model = IPAddress parent = forms.CharField(required=False, label='Search Within', widget=forms.TextInput(attrs={ 'placeholder': 'Prefix', })) family = forms.ChoiceField(required=False, choices=IP_FAMILY_CHOICES, label='Address Family') - vrf = forms.MultipleChoiceField(required=False, choices=ipaddress_vrf_choices, label='VRF', - widget=forms.SelectMultiple(attrs={'size': 6})) - tenant = forms.MultipleChoiceField(required=False, choices=tenant_choices, label='Tenant', - widget=forms.SelectMultiple(attrs={'size': 6})) + 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'), + label='Tenant') # @@ -470,14 +431,8 @@ class VLANGroupForm(forms.ModelForm, BootstrapMixin): fields = ['site', 'name', 'slug'] -def vlangroup_site_choices(): - site_choices = Site.objects.annotate(vlangroup_count=Count('vlan_groups')) - return [(s.slug, u'{} ({})'.format(s.name, s.vlangroup_count)) for s in site_choices] - - class VLANGroupFilterForm(forms.Form, BootstrapMixin): - site = forms.MultipleChoiceField(required=False, choices=vlangroup_site_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) + site = FilterChoiceField(choices=get_filter_choices(Site, id_field='slug', count_field='vlan_groups')) # @@ -555,21 +510,6 @@ class VLANBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): description = forms.CharField(max_length=100, required=False) -def vlan_site_choices(): - site_choices = Site.objects.annotate(vlan_count=Count('vlans')) - return [(s.slug, u'{} ({})'.format(s.name, s.vlan_count)) for s in site_choices] - - -def vlan_group_choices(): - group_choices = VLANGroup.objects.select_related('site').annotate(vlan_count=Count('vlans')) - return [(g.pk, u'{} ({})'.format(g, g.vlan_count)) for g in group_choices] - - -def vlan_tenant_choices(): - tenant_choices = Tenant.objects.annotate(vrf_count=Count('vlans')) - return [(t.slug, u'{} ({})'.format(t.name, t.vrf_count)) for t in tenant_choices] - - def vlan_status_choices(): status_counts = {} for status in VLAN.objects.values('status').annotate(count=Count('status')).order_by('status'): @@ -577,19 +517,11 @@ def vlan_status_choices(): return [(s[0], u'{} ({})'.format(s[1], status_counts.get(s[0], 0))) for s in VLAN_STATUS_CHOICES] -def vlan_role_choices(): - role_choices = Role.objects.annotate(vlan_count=Count('vlans')) - return [(r.slug, u'{} ({})'.format(r.name, r.vlan_count)) for r in role_choices] - - 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', - widget=forms.SelectMultiple(attrs={'size': 8})) - tenant = forms.MultipleChoiceField(required=False, choices=vlan_tenant_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) - status = forms.MultipleChoiceField(required=False, choices=vlan_status_choices) - role = forms.MultipleChoiceField(required=False, choices=vlan_role_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) + 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'), + label='VLAN Group') + tenant = FilterChoiceField(choices=get_filter_choices(Tenant, id_field='slug', count_field='vlans')) + status = FilterChoiceField(choices=vlan_status_choices) + role = FilterChoiceField(choices=get_filter_choices(Role, id_field='slug', count_field='vlans')) diff --git a/netbox/secrets/forms.py b/netbox/secrets/forms.py index 1e45fe163..e3ebb7500 100644 --- a/netbox/secrets/forms.py +++ b/netbox/secrets/forms.py @@ -2,10 +2,11 @@ from Crypto.Cipher import PKCS1_OAEP from Crypto.PublicKey import RSA from django import forms -from django.db.models import Count from dcim.models import Device -from utilities.forms import BootstrapMixin, BulkImportForm, CSVDataField, SlugField +from utilities.forms import ( + BootstrapMixin, BulkImportForm, CSVDataField, FilterChoiceField, SlugField, get_filter_choices, +) from .models import Secret, SecretRole, UserKey @@ -95,13 +96,8 @@ class SecretBulkEditForm(forms.Form, BootstrapMixin): name = forms.CharField(max_length=100, required=False) -def secret_role_choices(): - role_choices = SecretRole.objects.annotate(secret_count=Count('secrets')) - return [(r.slug, u'{} ({})'.format(r.name, r.secret_count)) for r in role_choices] - - class SecretFilterForm(forms.Form, BootstrapMixin): - role = forms.MultipleChoiceField(required=False, choices=secret_role_choices) + role = FilterChoiceField(choices=get_filter_choices(SecretRole, id_field='slug', count_field='secrets')) # diff --git a/netbox/tenancy/forms.py b/netbox/tenancy/forms.py index 40b5b5b1d..8bbd05372 100644 --- a/netbox/tenancy/forms.py +++ b/netbox/tenancy/forms.py @@ -1,8 +1,9 @@ from django import forms -from django.db.models import Count from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm -from utilities.forms import BootstrapMixin, BulkImportForm, CommentField, CSVDataField, SlugField +from utilities.forms import ( + BootstrapMixin, BulkImportForm, CommentField, CSVDataField, FilterChoiceField, SlugField, get_filter_choices, +) from .models import Tenant, TenantGroup @@ -74,12 +75,6 @@ class TenantBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): group = forms.TypedChoiceField(choices=bulkedit_tenantgroup_choices, coerce=int, required=False, label='Group') -def tenant_group_choices(): - group_choices = TenantGroup.objects.annotate(tenant_count=Count('tenants')) - return [(g.slug, u'{} ({})'.format(g.name, g.tenant_count)) for g in group_choices] - - class TenantFilterForm(BootstrapMixin, CustomFieldFilterForm): model = Tenant - group = forms.MultipleChoiceField(required=False, choices=tenant_group_choices, - widget=forms.SelectMultiple(attrs={'size': 8})) + group = FilterChoiceField(choices=get_filter_choices(TenantGroup, id_field='slug', count_field='tenants')) diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index 4cbc1028b..30989ddcf 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -3,6 +3,7 @@ import re from django import forms from django.core.urlresolvers import reverse_lazy +from django.db.models import Count from django.utils.encoding import force_text from django.utils.html import format_html from django.utils.safestring import mark_safe @@ -34,6 +35,27 @@ def add_blank_choice(choices): return ((None, '---------'),) + choices +def get_filter_choices(model, id_field='pk', select_related=[], count_field=None): + """ + Return a list of choices suitable for a ChoiceField. + + :param model: The base model to use for the queryset + :param id_field: Field to use as the object identifier + :param select_related: Any related tables to include + :param count: The field to use for a child COUNT() (optional) + :return: + """ + queryset = model.objects.all() + if select_related: + queryset = queryset.select_related(*select_related) + if count_field: + queryset = queryset.annotate(child_count=Count(count_field)) + return [(getattr(obj, id_field), u'{} ({})'.format(obj, obj.child_count)) for obj in queryset] + else: + return [(getattr(obj, id_field), u'{}'.format(obj)) for obj in queryset] + + + # # Widgets # @@ -222,6 +244,16 @@ class SlugField(forms.SlugField): self.widget.attrs['slug-source'] = slug_source +class FilterChoiceField(forms.MultipleChoiceField): + + def __init__(self, *args, **kwargs): + if 'required' not in kwargs: + kwargs['required'] = False + if 'widget' not in kwargs: + kwargs['widget'] = forms.SelectMultiple(attrs={'size': 6}) + super(FilterChoiceField, self).__init__(*args, **kwargs) + + # # Forms #