From 5c8c0e543fdf899d127c7604b84772295a0d5dce Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 16 Feb 2017 13:15:17 -0500 Subject: [PATCH] Implemented ArrayFieldSelectMultiple form widget --- netbox/dcim/forms.py | 24 +++++++++++++++++++++--- netbox/utilities/forms.py | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index ed6b87a50..64e8b57fa 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -1,6 +1,7 @@ import re from django import forms +from django.contrib.postgres.forms.array import SimpleArrayField from django.core.exceptions import ValidationError from django.db.models import Count, Q @@ -8,9 +9,9 @@ from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFi from ipam.models import IPAddress from tenancy.models import Tenant from utilities.forms import ( - APISelect, add_blank_choice, BootstrapMixin, BulkEditForm, BulkImportForm, CommentField, CSVDataField, - ExpandableNameField, FilterChoiceField, FlexibleModelChoiceField, Livesearch, SelectWithDisabled, SmallTextarea, - SlugField, + APISelect, add_blank_choice, ArrayFieldSelectMultiple, BootstrapMixin, BulkEditForm, BulkImportForm, CommentField, + CSVDataField, ExpandableNameField, FilterChoiceField, FlexibleModelChoiceField, Livesearch, SelectWithDisabled, + SmallTextarea, SlugField, ) from .formfields import MACAddressFormField @@ -248,11 +249,28 @@ class RackFilterForm(BootstrapMixin, CustomFieldFilterForm): # class RackReservationForm(BootstrapMixin, forms.ModelForm): + units = SimpleArrayField(forms.IntegerField(), widget=ArrayFieldSelectMultiple(attrs={'size': 10})) class Meta: model = RackReservation fields = ['units', 'description'] + def __init__(self, *args, **kwargs): + + super(RackReservationForm, self).__init__(*args, **kwargs) + + # Populate rack unit choices + self.fields['units'].widget.choices = self._get_unit_choices() + + def _get_unit_choices(self): + rack = self.instance.rack + reserved_units = [] + for resv in rack.reservations.exclude(pk=self.instance.pk): + for u in resv.units: + reserved_units.append(u) + unit_choices = [(u, {'label': str(u), 'disabled': u in reserved_units}) for u in rack.units] + return unit_choices + # # Manufacturers diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index f6e0b36b1..6eb11c208 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -169,6 +169,27 @@ class SelectWithDisabled(forms.Select): force_text(option_label)) +class ArrayFieldSelectMultiple(SelectWithDisabled, forms.SelectMultiple): + """ + MultiSelect widgets for a SimpleArrayField. Choices must be populated on the widget. + """ + + def __init__(self, *args, **kwargs): + self.delimiter = kwargs.pop('delimiter', ',') + super(ArrayFieldSelectMultiple, self).__init__(*args, **kwargs) + + def render_options(self, selected_choices): + # Split the delimited string of values into a list + if selected_choices: + selected_choices = selected_choices.split(self.delimiter) + return super(ArrayFieldSelectMultiple, self).render_options(selected_choices) + + def value_from_datadict(self, data, files, name): + # Condense the list of selected choices into a delimited string + data = super(ArrayFieldSelectMultiple, self).value_from_datadict(data, files, name) + return self.delimiter.join(data) + + class APISelect(SelectWithDisabled): """ A select widget populated via an API call