20645 CSVChoiceField use default if blank

This commit is contained in:
Arthur 2025-10-30 15:34:27 -07:00
parent 0b61d69e05
commit 90712fa865
2 changed files with 54 additions and 1 deletions

View File

@ -18,6 +18,20 @@ __all__ = (
) )
class CSVSelectWidget(forms.Select):
"""
Custom Select widget for CSV imports that treats blank values as omitted.
This allows model defaults to be applied when a CSV field is present but empty.
"""
def value_omitted_from_data(self, data, files, name):
# Check if value is omitted using parent behavior
if super().value_omitted_from_data(data, files, name):
return True
# Treat blank/empty strings as omitted to allow model defaults
value = data.get(name)
return value == '' or value is None
class CSVChoicesMixin: class CSVChoicesMixin:
STATIC_CHOICES = True STATIC_CHOICES = True
@ -29,8 +43,9 @@ class CSVChoicesMixin:
class CSVChoiceField(CSVChoicesMixin, forms.ChoiceField): class CSVChoiceField(CSVChoicesMixin, forms.ChoiceField):
""" """
A CSV field which accepts a single selection value. A CSV field which accepts a single selection value.
Treats blank CSV values as omitted to allow model defaults.
""" """
pass widget = CSVSelectWidget
class CSVMultipleChoiceField(CSVChoicesMixin, forms.MultipleChoiceField): class CSVMultipleChoiceField(CSVChoicesMixin, forms.MultipleChoiceField):
@ -46,7 +61,12 @@ class CSVMultipleChoiceField(CSVChoicesMixin, forms.MultipleChoiceField):
class CSVTypedChoiceField(forms.TypedChoiceField): class CSVTypedChoiceField(forms.TypedChoiceField):
"""
A CSV field for typed choice values.
Treats blank CSV values as omitted to allow model defaults.
"""
STATIC_CHOICES = True STATIC_CHOICES = True
widget = CSVSelectWidget
class CSVModelChoiceField(forms.ModelChoiceField): class CSVModelChoiceField(forms.ModelChoiceField):

View File

@ -4,6 +4,7 @@ from django.test import TestCase
from dcim.models import Site from dcim.models import Site
from netbox.choices import ImportFormatChoices from netbox.choices import ImportFormatChoices
from utilities.forms.bulk_import import BulkImportForm from utilities.forms.bulk_import import BulkImportForm
from utilities.forms.fields.csv import CSVSelectWidget
from utilities.forms.forms import BulkRenameForm from utilities.forms.forms import BulkRenameForm
from utilities.forms.utils import get_field_value, expand_alphanumeric_pattern, expand_ipaddress_pattern from utilities.forms.utils import get_field_value, expand_alphanumeric_pattern, expand_ipaddress_pattern
@ -448,3 +449,35 @@ class GetFieldValueTest(TestCase):
get_field_value(form, 'site'), get_field_value(form, 'site'),
None None
) )
class CSVSelectWidgetTest(TestCase):
"""
Validate that CSVSelectWidget treats blank values as omitted.
This allows model defaults to be applied when CSV fields are present but empty.
Related to issue #20645.
"""
def test_blank_value_treated_as_omitted(self):
"""Test that blank string values are treated as omitted"""
widget = CSVSelectWidget()
data = {'test_field': ''}
self.assertTrue(widget.value_omitted_from_data(data, {}, 'test_field'))
def test_none_value_treated_as_omitted(self):
"""Test that None values are treated as omitted"""
widget = CSVSelectWidget()
data = {'test_field': None}
self.assertTrue(widget.value_omitted_from_data(data, {}, 'test_field'))
def test_missing_field_treated_as_omitted(self):
"""Test that missing fields are treated as omitted"""
widget = CSVSelectWidget()
data = {}
self.assertTrue(widget.value_omitted_from_data(data, {}, 'test_field'))
def test_valid_value_not_omitted(self):
"""Test that valid values are not treated as omitted"""
widget = CSVSelectWidget()
data = {'test_field': 'valid_value'}
self.assertFalse(widget.value_omitted_from_data(data, {}, 'test_field'))