Merge pull request #20725 from netbox-community/20645-bulk-upload

20645 CSVChoiceField use default if blank
This commit is contained in:
bctiemann 2025-11-06 13:42:52 -05:00 committed by GitHub
commit 6c2a6d0e90
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
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:
STATIC_CHOICES = True
@ -29,8 +43,9 @@ class CSVChoicesMixin:
class CSVChoiceField(CSVChoicesMixin, forms.ChoiceField):
"""
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):
@ -46,7 +61,12 @@ class CSVMultipleChoiceField(CSVChoicesMixin, forms.MultipleChoiceField):
class CSVTypedChoiceField(forms.TypedChoiceField):
"""
A CSV field for typed choice values.
Treats blank CSV values as omitted to allow model defaults.
"""
STATIC_CHOICES = True
widget = CSVSelectWidget
class CSVModelChoiceField(forms.ModelChoiceField):

View File

@ -4,6 +4,7 @@ from django.test import TestCase
from dcim.models import Site
from netbox.choices import ImportFormatChoices
from utilities.forms.bulk_import import BulkImportForm
from utilities.forms.fields.csv import CSVSelectWidget
from utilities.forms.forms import BulkRenameForm
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'),
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'))