diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 701f6b74a..2821a1dc7 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -13,6 +13,7 @@ * [#7188](https://github.com/netbox-community/netbox/issues/7188) - Fix issue where select fields with `null_option` did not render or send the null option * [#7189](https://github.com/netbox-community/netbox/issues/7189) - Set connection factory for django-redis when Sentinel is in use * [#7193](https://github.com/netbox-community/netbox/issues/7193) - Fix prefix (flat) template issue when viewing child prefixes with prefixes available +* [#7205](https://github.com/netbox-community/netbox/issues/7205) - Fix issue where selected fields with `null_option` set were not added to applied filters * [#7209](https://github.com/netbox-community/netbox/issues/7209) - Allow unlimited API results when `MAX_PAGE_SIZE` is disabled --- diff --git a/netbox/utilities/forms/fields.py b/netbox/utilities/forms/fields.py index 05eaa9515..2561c2e22 100644 --- a/netbox/utilities/forms/fields.py +++ b/netbox/utilities/forms/fields.py @@ -477,3 +477,13 @@ class DynamicModelMultipleChoiceField(DynamicModelChoiceMixin, forms.ModelMultip """ filter = django_filters.ModelMultipleChoiceFilter widget = widgets.APISelectMultiple + + def clean(self, value): + """ + When null option is enabled and "None" is sent as part of a form to be submitted, it is sent as the + string 'null'. This will check for that condition and gracefully handle the conversion to a NoneType. + """ + if self.null_option is not None and settings.FILTERS_NULL_CHOICE_VALUE in value: + value = [v for v in value if v != settings.FILTERS_NULL_CHOICE_VALUE] + return [None, *value] + return super().clean(value) diff --git a/netbox/utilities/forms/utils.py b/netbox/utilities/forms/utils.py index 0121b250c..bb1f56c4d 100644 --- a/netbox/utilities/forms/utils.py +++ b/netbox/utilities/forms/utils.py @@ -1,6 +1,7 @@ import re from django import forms +from django.conf import settings from django.forms.models import fields_for_model from utilities.choices import unpack_grouped_choices @@ -120,13 +121,20 @@ def get_selected_values(form, field_name): if not hasattr(form, 'cleaned_data'): form.is_valid() filter_data = form.cleaned_data.get(field_name) - + field = form.fields[field_name] # Selection field - if hasattr(form.fields[field_name], 'choices'): + if hasattr(field, 'choices'): try: - choices = dict(unpack_grouped_choices(form.fields[field_name].choices)) + choices = unpack_grouped_choices(field.choices) + + if hasattr(field, 'null_option'): + # If the field has a `null_option` attribute set and it is selected, + # add it to the field's grouped choices. + if field.null_option is not None and None in filter_data: + choices.append((settings.FILTERS_NULL_CHOICE_VALUE, field.null_option)) + return [ - label for value, label in choices.items() if str(value) in filter_data + label for value, label in choices if str(value) in filter_data or None in filter_data ] except TypeError: # Field uses dynamic choices. Show all that have been populated.